Объекты пакета

92

Что такое объекты пакета, не столько концепция, сколько их использование?

Я попытался заставить пример работать, и единственная форма, которую я получил, была следующая:

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)
        }
    }
}

До сих пор я сделал следующие наблюдения:

package object _root_ { ... }

запрещено (что разумно),

package object x.y { ... }

также запрещено.

Кажется, что объект пакета должен быть объявлен в непосредственном родительском пакете, и, если он написан, как указано выше, требуется форма объявления пакета, разделенная фигурными скобками.

Они широко используются? Если да, то как?

Дон Маккензи
источник
1
@Brent, это отличный ресурс не только для статьи об объекте пакета. Я слышал об авторе, но не знал, что он написал этот тур по Scala, спасибо.
Дон Маккензи,

Ответы:

128

Обычно вы помещаете свой объект пакета в отдельный файл, вызываемый package.scalaв пакете, которому он соответствует. Вы также можете использовать синтаксис вложенного пакета, но это довольно необычно.

Основной вариант использования объектов пакета - это когда вам нужны определения в различных местах внутри вашего пакета, а также за пределами пакета, когда вы используете API, определенный пакетом. Вот пример:

// 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 = // ...

}

Теперь определения внутри этого объекта пакета доступны внутри всего пакета foo.bar. Кроме того, определения импортируются, когда импортируется кто-то за пределами этого пакета foo.bar._.

Таким образом, вы можете запретить клиенту API выдавать дополнительный импорт для эффективного использования вашей библиотеки - например, в scala-swing вам нужно написать

import swing._
import Swing._

иметь все добрые onEDTи неявные преобразования из Tuple2в Dimension.

Мориц
источник
13
Предупреждение: перегрузка метода не работает в объектах пакета.
ретроним
Меня лучше, почему было выбрано, что объект пакета должен быть определен на один уровень выше иерархии пакетов. Например, это означает, что вам нужно загрязнить виртуальный пакет orgили comпакет верхнего уровня своим объектом пакета, если вы хотите, чтобы он принадлежал вашему собственному корневому пакету, например org.foo. Я считаю, что если позволить определению быть прямо под пакетом, частью которого оно должно быть, было бы немного более подходящим языковым интерфейсом api.
Matanster 02
Обратите внимание , что , так как , по крайней мере Scala 2.10 и выше перегрузки делает работу в объектах пакета.
Джаспер-М,
58

Хотя ответ Морица точен, следует отметить еще одну вещь: объекты пакета являются объектами. Помимо прочего, это означает, что вы можете создавать их из признаков, используя смешанное наследование. Пример Морица можно было бы записать как

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 = // ...

}

Здесь управление версиями - это абстрактная черта, которая гласит, что объект пакета должен иметь метод «версии», тогда как JodaAliases и JavaAliases - это конкретные черты, содержащие удобные псевдонимы типов. Все эти черты могут быть повторно использованы множеством различных объектов пакета.

Дэйв Гриффит
источник
Тема в целом раскрывается, и кажется, что она полностью раскрыта, спасибо за еще один богатый пример.
Дон Маккензи
1
но их нельзя использовать в качестве валов, поэтому на самом деле они не объекты
Эдуардо Пареха Тобес
7

Можно поступить хуже, чем сразу обратиться к источнику. :)

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

Алекс Круз
источник
@Alex Cruise, спасибо, похоже, это говорит о том, что им нужен отдельный модуль компиляции (который, возможно, обходит ограничение пакета, разделенное фигурными скобками). Проблема в том, что мне нужен твердый совет пользователя, а не собственное предположение о том, как их использовать.
Дон Маккензи,
5

Основной вариант использования объектов пакета - это когда вам нужны определения в различных местах внутри вашего пакета, а также за пределами пакета, когда вы используете API, определенный пакетом.

Не так с Scala 3 , который планируется выпустить в середине 2020 года на основе Dotty , как здесь :

Определения верхнего уровня

На верхнем уровне можно писать всевозможные определения.
Объекты пакетов больше не нужны, будут прекращены.

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)
VonC
источник
Спасибо @VonC, я очень жду Scala 3 по этой и многим другим причинам. Я не особо использовал объекты пакета, но уверен, что буду использовать определения верхнего уровня.
Don Mackenzie