Oggetti pacchetto


92

Cosa sono gli oggetti pacchetto, non tanto il concetto ma il loro utilizzo?

Ho provato a far funzionare un esempio e l'unico modulo che ho avuto modo di lavorare è stato il seguente:

package object investigations {
    val PackageObjectVal = "A package object val"
}

package investigations {

    object PackageObjectTest {
        def main(args: Array[String]) {
            println("Referencing a package object val: " + PackageObjectVal)
        }
    }
}

Le osservazioni che ho fatto finora sono:

package object _root_ { ... }

non è consentito (il che è ragionevole),

package object x.y { ... }

è anche non consentito.

Sembra che un oggetto pacchetto debba essere dichiarato nel pacchetto genitore immediato e, se scritto come sopra, è richiesto il modulo di dichiarazione del pacchetto delimitato da parentesi graffe.

Sono di uso comune? Se é cosi, come?



1
@Brent, questa è una grande risorsa, non solo per l'articolo dell'oggetto pacchetto. Ho sentito parlare dell'autore ma non mi ero reso conto che avesse scritto questo tour alla Scala, grazie.
Don Mackenzie il

Risposte:


128

Normalmente metteresti il ​​tuo oggetto pacchetto in un file separato chiamato package.scala nel pacchetto a cui corrisponde. Puoi anche usare la sintassi del pacchetto annidato, ma è abbastanza insolito.

Il caso di utilizzo principale per gli oggetti del pacchetto è quando sono necessarie definizioni in vari punti all'interno del pacchetto e all'esterno del pacchetto quando si utilizza l'API definita dal pacchetto. Ecco un esempio:

// file: foo/bar/package.scala

package foo

package object bar {

  // package wide constants:
  def BarVersionString = "1.0"

  // or type aliases
  type StringMap[+T] = Map[String,T]

  // can be used to emulate a package wide import
  // especially useful when wrapping a Java API
  type DateTime = org.joda.time.DateTime

  type JList[T] = java.util.List[T]

  // Define implicits needed to effectively use your API:
  implicit def a2b(a: A): B = // ...

}

Ora le definizioni all'interno di quell'oggetto pacchetto sono disponibili all'interno dell'intero pacchetto foo.bar. Inoltre le definizioni vengono importate quando qualcuno al di fuori di quel pacchetto importafoo.bar._ .

In questo modo puoi evitare di richiedere al client API di emettere importazioni aggiuntive per usare la tua libreria in modo efficace - ad esempio in scala-swing devi scrivere

import swing._
import Swing._

avere tutte le bontà simili onEDTe le conversioni implicite da Tuple2a Dimension.


13
Un avvertimento: il sovraccarico del metodo non funziona negli oggetti del pacchetto.
retronym

Mi batte il motivo per cui è stato scelto di definire l'oggetto pacchetto di un livello superiore nella gerarchia del pacchetto. Ad esempio, questo significa che devi inquinare il pacchetto virtuale orgo di comprimo livello con il tuo oggetto pacchetto se desideri che appartenga al tuo pacchetto root, ad es org.foo. Trovo che consentire alla definizione di essere direttamente sotto il pacchetto di cui dovrebbe far parte - sarebbe stata un'interfaccia API del linguaggio leggermente più appropriata.
matanster

58

Mentre la risposta di Moritz è azzeccata, un'altra cosa da notare è che gli oggetti pacchetto sono oggetti. Tra le altre cose, questo significa che puoi costruirli dai tratti, usando l'ereditarietà mista. L'esempio di Moritz potrebbe essere scritto come

package object bar extends Versioning 
                          with JodaAliases 
                          with JavaAliases {

  // package wide constants:
  override val version = "1.0"

  // or type aliases
  type StringMap[+T] = Map[String,T]

  // Define implicits needed to effectively use your API:
  implicit def a2b(a: A): B = // ...

}

Qui Versioning è un tratto astratto, che dice che l'oggetto pacchetto deve avere un metodo "version", mentre JodaAliases e JavaAliases sono tratti concreti contenenti utili alias di tipo. Tutti questi tratti possono essere riutilizzati da molti diversi oggetti del pacchetto.


L'intero argomento si sta aprendo molto e sembra essere abituato al suo pieno potenziale, grazie per un altro ricco esempio.
Don Mackenzie

1
ma non possono essere usati come vals, quindi non sono realmente oggetti
Eduardo Pareja Tobes

7

@Alex Cruise, grazie, questo sembra suggerire che hanno bisogno di un'unità di compilazione separata (che forse aggira la restrizione del pacchetto delimitata da parentesi graffe). Il problema è che voglio alcuni solidi consigli per gli utenti piuttosto che le mie congetture su come usarli.
Don Mackenzie

5

Il caso d'uso principale per gli oggetti del pacchetto è quando hai bisogno di definizioni in vari punti all'interno del tuo pacchetto e all'esterno del pacchetto quando usi l'API definita dal pacchetto.

Non così con Scala 3 , previsto per la metà del 2020, basato su Dotty , come qui :

Definizioni di Toplevel

Tutti i tipi di definizioni possono essere scritti a livello superiore.
Gli oggetti del pacchetto non sono più necessari, verranno eliminati gradualmente.

package p 

type Labelled[T] = (String, T) 
val a: Labelled[Int] = ("count", 1) 
def b = a._2 
def hello(name: String) = println(i"hello, $name)

Grazie @VonC, non vedo l'ora che arrivi Scala 3 per questo e molti altri motivi. Non ho fatto molto uso degli oggetti pacchetto ma sono sicuro che userò definizioni di primo livello.
Don Mackenzie
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.