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?



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.


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.


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

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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.