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

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

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

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

Обычно вы помещаете свой пакетный объект в отдельный файл с именем 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.

Ответ 2

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

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

}

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

Ответ 4

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

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

Определения Toplevel

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

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)