39

从 2.10 开始,-Xlint抱怨在包对象内部定义的类。但为什么?在包对象中定义一个类应该与在一个单独的包中定义具有相同名称的类完全相同,除了方便得多。

在我看来,Scala 的严重设计缺陷之一是无法在文件的顶层放置除类实体(例如变量声明、函数定义)之外的任何内容。相反,您被迫将它们放入一个单独的“包对象”(通常package.scala在也是。我看不出 Scala 在概念上为什么不能在顶层允许它在较低级别允许的任何东西,并且任何非类的东西都会自动放入包对象中,这样用户就不必担心它了。

例如,在我的例子中,我有一个util包,在它下面有许多子包(util.ioutil.textutil.timeutil.osutil.mathutil.distances等),这些子包将函数、类和有时是语义相关的变量的异构集合分组。我目前将所有各种函数、类等存储在目录package object中一个名为io.scalatext.scala其他的文件中util。这很好用,而且非常方便,因为可以混合函数和类,例如,我可以执行以下操作:

package object math {
  // Coordinates on a sphere

  case class SphereCoord(lat: Double, long: Double) { ... }

  // great-circle distance between two points
  def spheredist(a: SphereCoord, b: SphereCoord) = ...

  // Area of rectangle running along latitude/longitude lines
  def rectArea(topleft: SphereCoord, botright: SphereCoord) = ...

  // ...
  // ...

  // Exact-decimal functions
  class DecimalInexactError extends Exception

  // Format floating point value in decimal, error if can't do exactly
  formatDecimalExactly(val num: Double) = ...

  // ...
  // ...
}

如果没有这个,我将不得不根据乐趣与类而不是语义来不方便地拆分代码。我想,另一种选择是将它们放在一个普通的对象中——这有点违背了首先拥有包装对象的目的。

4

3 回答 3

31

但为什么?在包对象中定义一个类应该完全等同于在一个单独的包中定义具有相同名称的类,

恰恰。语义(当前)是相同的,因此如果您喜欢在包对象中定义一个类,那么应该有充分的理由。但现实情况是,至少有一个很好的理由不(继续阅读)。

除了方便很多

怎样更方便?如果你这样做:

package object mypkg {
  class MyClass
}

您也可以执行以下操作:

package mypkg {
  class MyClass
}

您甚至可以在此过程中保存一些字符 :)

现在,不要过度使用包对象的一个​​很好的具体原因是,虽然包是打开的,包对象不是。一个常见的场景是让您的代码在多个项目之间分派,每个项目在同一个包中定义类。这里没问题。另一方面,一个包对象(像任何对象一样)是封闭的(正如规范所说的“每个包只能有一个包对象”)。换句话说,您将只能在一个中定义一个包对象你的项目。如果你试图在两个不同的项目中为同一个包定义一个包对象,就会发生不好的事情,因为你最终会得到同一个 JVM 类的两个不同版本(在我们的例子中,你会得到两个“mypkg.类”文件)。根据具体情况,您最终可能会导致编译器抱怨它找不到您在包对象的第一个版本中定义的内容,或者出现“错误的符号引用”错误,甚至可能出现运行时错误。这是包对象的一般限制,因此您必须注意它。在包对象中定义类的情况下,解决方案很简单:不要这样做(与仅将类定义为顶级相比,您不会获得任何实质性的东西)。对于别名、vals 和 vars 类型,我们不

于 2013-06-24T12:56:28.430 回答
2

我还没有找到一个很好的答案来解释为什么这个语义上等价的操作会产生一个 lint 警告。我认为这是一个 lint bug。我发现唯一不能放在包对象中(相对于普通包中)的是实现main(或extends App)的对象。

请注意,-Xlint 还抱怨在包对象内声明的隐式类,即使它们不能在包范围内声明。(有关隐式类的规则,请参见http://docs.scala-lang.org/overviews/core/implicit-classes.html 。)

于 2014-01-08T19:45:23.827 回答
-1

我想出了一个技巧,它允许包对象的所有好处而不会抱怨弃用。代替

package object foo {
  ...
}

你可以做

protected class FooPackage {
  ...
}

package object foo extends FooPackage { }

工作相同,但没有抱怨。明显的迹象表明投诉本身是虚假的。

于 2014-01-09T02:09:16.680 回答