Scala特性继承奇怪的行为

时间:2018-01-11 21:15:46

标签: scala traits

有人可以解释为什么这个程序打印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)

2 个答案:

答案 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中的私人成员称为widthheight。它将它们初始化为零。然后,在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会在它们出现之前看到默认的零widthheight值组。正如您已经发现的那样,使用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 * heightwidthheight都尚未初始化,因此它们的值为零。这意味着area = 0 * 0 = 0

之后,widthheight设置为size的值,但此时区域为时已晚。

它与lazy val一起使用,因为惰性值检查它们是否在访问时已初始化,如果没有则初始化。常规的val没有这样的检查。