为什么人们在Scala中的另一个对象中定义类,特征,对象?

时间:2011-01-13 14:47:16

标签: scala lift

好的,我会解释为什么我会问这个问题。这几天我开始阅读Lift 2.2源代码。 如果你以前读过电梯源代码就好了。

在Lift中,我发现,定义内部类和内部特征的使用非常频繁。

对象Menu有2个内部特征和4个内部类。 object Loc有18个内部类,5个内部特征,7个内部对象。

有很多代码都是这样写的。我想知道为什么作者这样写。

  • 是因为它是作者的 个人品味或强大的用途 语言功能?
  • 这种方式是否有任何权衡 用法?

4 个答案:

答案 0 :(得分:22)

在2.8之前,你必须在包和对象之间进行选择。包的问题是它们不能自己包含方法或val。所以你必须将所有这些放在另一个对象中,这可能会变得很尴尬。观察:

object Encrypt {
  private val magicConstant = 0x12345678
  def encryptInt(i: Int) = i ^ magicConstant
  class EncryptIterator(ii: Iterator[Int]) extends Iterator[Int] {
    def hasNext = ii.hasNext
    def next = encryptInt(ii.next)
  }
}

现在,您可以import Encrypt._访问方法encryptInt以及类EncryptIterator。方便!

相比之下,

package encrypt {
  object Encrypt {
    private[encrypt] val magicConstant = 0x12345678
    def encryptInt(i: Int) = i ^ magicConstant
  }
  class EncryptIterator(ii: Iterator[Int]) extends Iterator[Int] {
    def hasNext = ii.hasNext
    def next = Encrypt.encryptInt(ii.next)
  }
}

这不是巨大的差异,但它会让用户同时导入encrypt._encrypt.Encrypt._,或者必须一遍又一遍地写Encrypt.encryptInt。为什么不直接使用对象,就像在第一个模式中一样? (实际上没有性能损失,因为嵌套类实际上并不是Java内部类;它们只是JVM知道的常规类,但是用花哨的名称告诉你它们是嵌套的。)

在2.8中,您可以拥有自己的蛋糕并将其吃掉:将该东西称为包对象,编译器将为您重写代码,因此它实际上看起来像引擎盖下的第二个示例(除了对象{{1实际上在内部被称为Encrypt,但在命名空间方面表现得像第一个例子 - vals和defs就在那里而不需要额外的导入。

因此,在2.8之前启动的项目经常使用对象来封装大量的东西,就好像它们是一个包。 2.8之后,其中一个主要动机已被删除。 (但要明确的是,使用一个对象仍然没有受到伤害;更多的是它在概念上具有误导性,而不是它对性能或其他方面产生负面影响。)

(P.S。请不要尝试实际加密任何方式除非作为示例或笑话!)

答案 1 :(得分:4)

当您想要使用抽象类型变量时,有时需要在对象中放置类,特征和对象,例如, http://programming-scala.labs.oreilly.com/ch12.html#_parameterized_types_vs_abstract_types

答案 2 :(得分:3)

两者都可以。除此之外,内部类/特征的实例可以访问其父类的变量。必须使用父实例创建内部类,父实例是外部类型的实例。

在其他情况下,它可能只是一种对密切相关的事物进行分组的方式,就像在object示例中一样。请注意,特征LocParam是密封的,这意味着所有子类必须位于同一个编译单元/文件中。

答案 3 :(得分:2)

sblundy有一个不错的答案。要添加的一件事是,只有使用Scala 2.8,您才能拥有包对象,这些包对象允许您在包命名空间中对类似的事物进行分组,而无需创建完全独立的对象。出于这个原因,我将更新我的Lift Modules提议,以使用包对象而不是简单的对象。