wake-up-neo.com

Objekte verpacken

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?

89
Don Mackenzie

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.

125
Moritz

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.

56
Dave Griffith
7
Alex Cruise

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