Was sind Paketobjekte, nicht so sehr das Konzept, sondern ihre Verwendung?
Ich habe versucht, ein Beispiel zum Laufen zu bringen, und das einzige Formular, mit dem ich arbeiten konnte, war das Folgende:
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)
}
}
}
Bisherige Beobachtungen sind:
package object _root_ { ... }
ist nicht erlaubt (was vernünftig ist),
package object x.y { ... }
ist auch nicht erlaubt.
Es scheint, dass ein Paketobjekt im unmittelbaren übergeordneten Paket deklariert werden muss und, wenn es wie oben beschrieben ist, das durch Klammern getrennte Paketdeklarationsformular erforderlich ist.
Sind sie allgemein gebräuchlich? Wenn das so ist, wie?
Normalerweise würden Sie Ihr Paketobjekt in einer separaten Datei mit dem Namen package.scala
In dem Paket ablegen, dem es entspricht. Sie können auch die verschachtelte Paketsyntax verwenden, dies ist jedoch recht ungewöhnlich.
Der Hauptanwendungsfall für Paketobjekte besteht darin, dass Sie Definitionen an verschiedenen Stellen in Ihrem Paket sowie außerhalb des Pakets benötigen, wenn Sie die vom Paket definierte API verwenden. Hier ist ein Beispiel:
// 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 = // ...
}
Jetzt sind die Definitionen in diesem Paketobjekt im gesamten Paket verfügbar foo.bar
. Außerdem werden die Definitionen importiert, wenn jemand außerhalb dieses Pakets foo.bar._
Importiert.
Auf diese Weise können Sie verhindern, dass der API-Client zusätzliche Importe ausführen muss, um Ihre Bibliothek effektiv zu nutzen - z. in scala-swing musst du schreiben
import swing._
import Swing._
um alle Güte wie onEDT
und implizite Konvertierungen von Tuple2
nach Dimension
zu haben.
Während Moritz's Antwort genau richtig ist, ist eine weitere Sache zu beachten, dass Paketobjekte Objekte sind. Dies bedeutet unter anderem, dass Sie sie mithilfe von Mix-In-Vererbung aus Merkmalen aufbauen können. Moritz 'Beispiel könnte geschrieben werden als
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 = // ...
}
Hier ist die Versionierung ein abstraktes Merkmal, das besagt, dass das Paketobjekt eine "Versions" -Methode haben muss, während JodaAliases und JavaAliases konkrete Merkmale sind, die handliche Typaliase enthalten. Alle diese Eigenschaften können von vielen verschiedenen Paketobjekten wiederverwendet werden.
Sie könnten schlimmeres tun, als direkt zur Quelle zu gehen. :)
https://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/library/scala/package.scala
Der Hauptanwendungsfall für Paketobjekte besteht darin, dass Sie Definitionen an verschiedenen Stellen in Ihrem Paket sowie außerhalb des Pakets benötigen, wenn Sie die vom Paket definierte API verwenden.
Nicht so bei Scala 3 , dessen Veröffentlichung für Mitte 2020 geplant ist, basierend auf Dotty , als hier :
Toplevel-Definitionen
Alle Arten von Definitionen können auf der obersten Ebene geschrieben werden.
Paketobjekte werden nicht mehr benötigt, werden auslaufen.
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)