有人可以解释为什么这个程序打印0而不是4?
trait Rectangular {
def width: Int
def height: Int
val area = width * height
}
case class Square(val size: Int) extends Rectangular {
val width = size
val height = size
}
print(Square(2).area)
但是当我使vals懒惰时它会起作用
trait Rectangular {
def width: Int
def height: Int
val area = width * height
}
case class Square(val size: Int) extends Rectangular {
lazy val width = size
lazy val height = size
}
print(Square(2).area)
答案 0 :(得分:2)
这是Scala构建val
成员的方式的一个不幸的问题。
trait Rectangular {
def width: Int
def height: Int
val area = width * height
}
case class Square(val size: Int) extends Rectangular {
val width = size
val height = size
}
此处,在内部,Scala会将Square
中的私人成员称为width
和height
。它将它们初始化为零。然后,在Square
构造函数中,它设置它们。基本上,它做了一些接近这个Java代码的东西。
public abstract class Rectangular {
private int area;
public Rectangular() {
area = width() * height();
}
public abstract int width();
public abstract int height();
public int area() { return area; }
}
public class Square extends Rectangular {
private int width, height;
public Square(int size) {
Rectangular();
width = size;
height = size;
}
public int width() { return width; }
public int height() { return width; }
}
请注意Rectangular
构造函数在Square
构造函数之前调用,因此area
会在它们出现之前看到默认的零width
和height
值组。正如您已经发现的那样,使用lazy val
可以解决问题。
trait Rectangular {
def width: Int
def height: Int
val area = width * height
}
case class Square(val size: Int) extends Rectangular {
lazy val width = size
lazy val height = size
}
或者,您可以使用早期初始化程序语法强制以正确的顺序写入值。
trait Rectangular {
def width: Int
def height: Int
val area = width * height
}
case class Square(val size: Int) extends {
val width = size
val height = size
} with Rectangular
lazy val
解决方案通常更可取,因为它可以减少意外情况。
有关详情,请参阅Scala FAQ有关此特定主题的内容。
答案 1 :(得分:1)
问题是你的特质中的val会在你班级的val之前被初始化。
因此,当您调用构造函数时,首先会初始化area
。它将其值设置为width * height
。 width
和height
都尚未初始化,因此它们的值为零。这意味着area = 0 * 0 = 0
。
之后,width
和height
设置为size
的值,但此时区域为时已晚。
它与lazy val
一起使用,因为惰性值检查它们是否在访问时已初始化,如果没有则初始化。常规的val没有这样的检查。