Objetos de pacote

92

O que são objetos de pacote, não tanto o conceito, mas seu uso?

Tentei fazer um exemplo funcionar e a única forma que consegui funcionar foi a seguinte:

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

As observações que fiz até agora são:

package object _root_ { ... }

não é permitido (o que é razoável),

package object x.y { ... }

também não é permitido.

Parece que um objeto de pacote deve ser declarado no pacote pai imediato e, se escrito como acima, o formulário de declaração de pacote delimitado por chaves é necessário.

Eles são de uso comum? Se sim, como?

Don Mackenzie
fonte
1
@Brent, este é um ótimo recurso, não apenas para o artigo de objeto de pacote. Já ouvi falar do autor, mas não sabia que ele havia escrito esse tour Scala, obrigado.
Don Mackenzie

Respostas:

128

Normalmente, você colocaria seu objeto de pacote em um arquivo separado chamado package.scalano pacote ao qual ele corresponde. Você também pode usar a sintaxe de pacote aninhado, mas isso é bastante incomum.

O principal caso de uso para objetos de pacote é quando você precisa de definições em vários lugares dentro do seu pacote, bem como fora do pacote, ao usar a API definida pelo pacote. Aqui está um exemplo:

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

}

Agora, as definições dentro desse objeto de pacote estão disponíveis dentro de todo o pacote foo.bar. Além disso, as definições são importadas quando alguém de fora desse pacote importa foo.bar._.

Desta forma, você pode evitar que o cliente API emita importações adicionais para usar sua biblioteca de forma eficaz - por exemplo, no scala-swing, você precisa escrever

import swing._
import Swing._

ter todas as onEDTconversões semelhantes e implícitas de bondade de Tuple2para Dimension.

Moritz
fonte
13
Aviso: a sobrecarga de método não funciona em objetos de pacote.
retrônimo de
Não consigo entender por que foi escolhido que o objeto de pacote deveria ser definido um nível acima na hierarquia de pacotes. Por exemplo, isso significa que você precisa poluir o pacote virtual orgou de comnível superior com o seu objeto de pacote se desejar que ele pertença ao seu próprio pacote raiz org.foo. Eu acho que permitir que a definição esteja diretamente sob o pacote do qual ela deveria fazer parte - teria sido uma interface API de linguagem um pouco mais adequada.
matanster
58

Embora a resposta de Moritz seja correta, uma coisa adicional a ser observada é que os objetos de pacote são objetos. Entre outras coisas, isso significa que você pode construí-los a partir de características, usando a herança combinada. O exemplo de Moritz poderia ser escrito como

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

}

Aqui, o controle de versão é uma característica abstrata, que diz que o objeto de pacote deve ter um método de "versão", enquanto JodaAliases e JavaAliases são características concretas contendo apelidos de tipo úteis. Todas essas características podem ser reutilizadas por muitos objetos de pacote diferentes.

Dave Griffith
fonte
O tópico todo está se abrindo muito e parece estar acostumado com todo o seu potencial, obrigado por outro exemplo rico.
Don Mackenzie
1
mas não podem ser usados ​​como vals, então não são realmente objetos
Eduardo Pareja Tobes 8/08
7

Você poderia fazer pior do que ir direto à fonte. :)

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
fonte
@Alex Cruise, obrigado, isso parece sugerir que eles precisam de uma unidade de compilação separada (que talvez contorne a restrição de pacote delimitado por chave). O problema é que quero alguns conselhos de usuário sólidos, em vez de minhas próprias conjecturas sobre como usá-los.
Don Mackenzie
5

O principal caso de uso para objetos de pacote é quando você precisa de definições em vários lugares dentro do seu pacote, bem como fora do pacote, ao usar a API definida pelo pacote.

Não é assim com Scala 3 , programado para ser lançado em meados de 2020, baseado em Dotty , como aqui :

Definições de nível superior

Todos os tipos de definições podem ser escritos no nível superior.
Objetos de pacote não são mais necessários, serão eliminados.

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
fonte
Obrigado @VonC, estou realmente ansioso pelo Scala 3 por este e muitos outros motivos. Não tenho feito muito uso de objetos de pacote, mas tenho certeza de que usarei definições de nível superior.
Don Mackenzie