Programming in Scala,Third Edition,Martin Odersky“在第449页给出了这个例子:
trait RationalTrait {
val numerArg: Int
val denomArg: Int
require(denomArg != 0)
private val g = gcd(numerArg, denomArg)
val numer = numerArg / g
val denom = denomArg / g
private def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)
override def toString = numer + "/" + denom
}
然后它解释了以下代码失败,因为trait在匿名类之前被初始化,因此,denomArg仍为0。
new RationalTrait {
val numerArg = 4
val denomArg = 24
}
它提供了两种解决方案。一种解决方案是使用预先初始化的字段:
new {
val numerArg = 4
val denomArg = 24
} with RationalTrait
第二种解决方案是修改特性以使用延迟值,如下所示:
trait LazyRationalTrait {
val numerArg: Int
val denomArg: Int
lazy val numer = numerArg / g
lazy val denom = denomArg / g
override def toString = numer + "/" + denom
private lazy val g = {
require(denomArg != 0)
gcd(numerArg, denomArg)
}
private def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)
}
new LazyRationalTrait {
lazy val numerArg = 4
lazy val denomArg = 24
}
然而,这个更简单的解决方案也有效。我想知道为什么他们没有提到这个解决方案。这有什么缺点吗?
new RationalTrait {
lazy val numerArg = 4
lazy val denomArg = 24
}
答案 0 :(得分:1)
我认为你的解决方案与第二个选项相比更加一致(在Scala编程的第453页提到,3d ed。):
trait A {
val x: Int
lazy val y: Int = x
val z: Int = y
}
new A { val x = 1 }.y // ==0 ?!!!
new A { val x = 1 }.z // ==0 ?!!!
VS
trait A {
val x: Int
val y: Int = x
val z: Int = y
}
new A { lazy val x = 1 }.y // ==1
new A { lazy val x = 1 }.z // ==1
至少我不需要记住懒惰的val
不能用于基类中的非惰性构造。查看字节码我没有看到你的方法有任何明显的问题,因为在后一种情况下x
被有效地定义为def
,所以它在基类中初始化时已经存在。