Scala 特征继承奇怪的行为
Scala trait inheritance strange behavior
谁能解释为什么这个程序打印的是 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)
这是 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。
问题是你特征中的 val 在你 class 中的 val 之前被初始化。
所以当你调用构造函数时,首先 area
被初始化。它将其值设置为 width * height
。 width
和 height
都尚未初始化,因此它们的值为零。这意味着 area = 0 * 0 = 0
.
在那之后,width
和 height
被设置为 size
的值,但是此时对于区域来说已经太晚了。
它适用于 lazy val
,因为惰性值会在访问时检查它们是否已被初始化,如果没有则自行初始化。常规 val 没有此类检查。
谁能解释为什么这个程序打印的是 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)
这是 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。
问题是你特征中的 val 在你 class 中的 val 之前被初始化。
所以当你调用构造函数时,首先 area
被初始化。它将其值设置为 width * height
。 width
和 height
都尚未初始化,因此它们的值为零。这意味着 area = 0 * 0 = 0
.
在那之后,width
和 height
被设置为 size
的值,但是此时对于区域来说已经太晚了。
它适用于 lazy val
,因为惰性值会在访问时检查它们是否已被初始化,如果没有则自行初始化。常规 val 没有此类检查。