Que sont les objets de package, pas tant le concept que leur utilisation?
J'ai essayé de faire fonctionner un exemple et le seul formulaire que j'ai eu à travailler était le suivant:
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)
}
}
}
Les observations que j'ai faites jusqu'à présent sont les suivantes:
package object _root_ { ... }
est refusée (ce qui est raisonnable),
package object x.y { ... }
est également interdit.
Il semble qu'un objet package doit être déclaré dans le package parent immédiat et, s'il est écrit comme ci-dessus, le formulaire de déclaration de package délimité par des accolades est requis.
Sont-ils d'usage courant? Si c'est le cas, comment?
Normalement, vous placez votre objet package dans un fichier séparé appelé package.scala
dans le package auquel il correspond. Vous pouvez également utiliser la syntaxe du package imbriqué, mais cela est assez inhabituel.
Le cas d'utilisation principal pour les objets de package est lorsque vous avez besoin de définitions à divers endroits à l'intérieur de votre package ainsi qu'à l'extérieur du package lorsque vous utilisez l'API définie par le package. Voici un exemple:
// 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 = // ...
}
Maintenant, les définitions à l'intérieur de cet objet de package sont disponibles dans l'ensemble du package foo.bar
. De plus, les définitions sont importées lorsque quelqu'un en dehors de ce package importe foo.bar._
.
De cette façon, vous pouvez éviter d'exiger que le client API émette des importations supplémentaires pour utiliser efficacement votre bibliothèque - par exemple en scala-swing, vous devez écrire
import swing._
import Swing._
d'avoir toutes les qualités comme onEDT
et les conversions implicites de Tuple2
à Dimension
.
Bien que la réponse de Moritz soit parfaite, une chose supplémentaire à noter est que les objets de package sont des objets. Entre autres choses, cela signifie que vous pouvez les construire à partir de traits, en utilisant l'héritage mix-in. L'exemple de Moritz pourrait s'écrire
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 = // ...
}
Ici, le contrôle de version est un trait abstrait, qui dit que l'objet package doit avoir une méthode "version", tandis que JodaAliases et JavaAliases sont des traits concrets contenant des alias de type pratiques. Tous ces traits peuvent être réutilisés par de nombreux objets de package différents.
Le cas d'utilisation principal pour les objets de package est lorsque vous avez besoin de définitions à divers endroits à l'intérieur de votre package ainsi qu'à l'extérieur du package lorsque vous utilisez l'API définie par le package.
Ce n'est pas le cas avec Scala 3 , dont la sortie est prévue mi-2020, basé sur Dotty , comme ici :
Définitions de Toplevel
Toutes sortes de définitions peuvent être écrites au niveau supérieur.
Les objets de package ne sont plus nécessaires, seront supprimés progressivement
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)