web-dev-qa-db-fra.com

Objets de package

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?

89
Don Mackenzie

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.

125
Moritz

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.

56
Dave Griffith

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)
3
VonC