Czym są obiekty pakietu, nie tyle koncepcja, ile ich użycie?
Próbowałem uzyskać działający przykład i jedyna forma, jaką otrzymałem, była następująca:
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)
}
}
}
Obserwacje, które poczyniłem do tej pory to:
package object _root_ { ... }
jest niedozwolone (co jest rozsądne),
package object x.y { ... }
jest również niedozwolone.
Wydaje się, że obiekt pakietu musi być zadeklarowany w bezpośrednim pakiecie nadrzędnym, a jeśli jest napisany jak wyżej, wymagany jest formularz deklaracji pakietu rozdzielanego nawiasami klamrowymi.
Czy są w powszechnym użyciu? Jeśli tak to jak?
Odpowiedzi:
Zwykle umieściłbyś obiekt pakietu w oddzielnym pliku o nazwie
package.scala
w pakiecie, któremu odpowiada. Możesz także użyć składni pakietu zagnieżdżonego, ale jest to dość nietypowe.Głównym przypadkiem użycia obiektów pakietu jest sytuacja, w której potrzebujesz definicji w różnych miejscach wewnątrz pakietu, a także poza pakietem, gdy używasz interfejsu API zdefiniowanego przez pakiet. Oto przykład:
// 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 = // ... }
Teraz definicje wewnątrz tego obiektu pakietu są dostępne w całym pakiecie
foo.bar
. Ponadto definicje są importowane, gdy importuje się ktoś spoza tego pakietufoo.bar._
.W ten sposób możesz zapobiec wymaganiu od klienta API wykonywania dodatkowych importów, aby efektywnie korzystać z biblioteki - np. W scala-swing musisz napisać
import swing._ import Swing._
mieć wszystkie podobne dobroci
onEDT
i ukryte konwersje zTuple2
naDimension
.źródło
org
lubcom
pakiet najwyższego poziomu swoim obiektem pakietu, jeśli chcesz, aby należał on do twojego własnego pakietu głównego, nporg.foo
. Uważam, że zezwolenie na umieszczenie definicji bezpośrednio pod pakietem, którego powinna być częścią, byłoby nieco bardziej odpowiednim interfejsem API w języku.Chociaż odpowiedź Moritza jest trafna, jedną dodatkową rzeczą, na którą należy zwrócić uwagę, jest to, że obiekty pakietu są obiektami. Oznacza to między innymi, że możesz budować je na podstawie cech, korzystając z dziedziczenia mieszanego. Przykład Moritza można zapisać jako
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 = // ... }
W tym przypadku wersjonowanie jest cechą abstrakcyjną, która mówi, że obiekt pakietu musi mieć metodę "wersji", podczas gdy aliasy Joda i aliasy Java są konkretnymi cechami zawierającymi przydatne aliasy typów. Wszystkie te cechy można ponownie wykorzystać w wielu różnych obiektach opakowań.
źródło
Mogłeś zrobić gorzej niż udać się prosto do źródła. :)
https://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/library/scala/package.scala
https://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/library/scala/collection/immutable/package.scala
źródło
Inaczej jest w przypadku Scali 3 , która ma zostać wydana w połowie 2020 roku, oparta na Dotty , jak tutaj :
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)
źródło