好的,我会解释为什么我会问这个问题。这几天我开始阅读Lift 2.2源代码。 如果你以前读过电梯源代码就好了。
在Lift中,我发现,定义内部类和内部特征的使用非常频繁。
对象Menu有2个内部特征和4个内部类。 object Loc有18个内部类,5个内部特征,7个内部对象。
有很多代码都是这样写的。我想知道为什么作者这样写。
答案 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提议,以使用包对象而不是简单的对象。