包对象


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之旅,谢谢。
唐·麦肯齐

Answers:


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._

具有onEDTTuple2到的所有相似和隐式转换Dimension


13
警告:方法重载在包对象中不起作用。
Retronym 2010年

让我感到困惑的是,为什么选择将包对象定义为包层次结构的上一级。例如,这意味着如果您希望虚拟对象orgcom顶级软件包属于您自己的根软件包,则需要用它来污染它org.foo。我发现允许将定义直接包含在包中,应该是其中一部分-本来应该是更合适的语言api接口。
matanster

58

尽管Moritz的答案很明确,但要注意的另一件事是,包对象是对象。除其他外,这意味着您可以使用混合继承从特征中构建它们。莫里茨的例子可以写成

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是包含方便的类型别名的具体特征。所有这些特征都可以被许多不同的包对象重用。


整个话题开放了很多,并且由于另一个丰富的例子,它似乎已被充分利用。
唐·麦肯齐

1
但它们不能被用作丘壑,所以他们没有真正对象
爱德华多·帕雷哈Tobes

7

@Alex Cruise,谢谢,这似乎表明他们需要一个单独的编译单元(可能绕过括号分隔的软件包限制)。问题是我想要一些可靠的用户建议,而不是我自己对如何使用它们的猜测。
唐·麦肯齐

5

包对象的主要用例是,当您使用包定义的API时,需要在包内以及包外的各个地方进行定义。

并非如此,斯卡拉3,按计划将发布中期到2020年,基于斑点狗,因为在这里

顶级定义

各种定义都可以写在顶层。
包对象不再需要,将被淘汰。

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,由于这个原因和许多其他原因,我真的很期待Scala 3。我没有太多使用包对象,但是我可以肯定会使用顶级定义。
Don Mackenzie
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.