【问题标题】:Scala trait inheritance strange behaviorScala trait 继承奇怪的行为
【发布时间】:2018-01-11 21:15:46
【问题描述】:

谁能解释为什么这个程序打印的是 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)

【问题讨论】:

  • 这是因为您将区域定义为val。使用def 就可以了。
  • 它与 def 或 lazy val 一起工作的事实并不能解释这种行为

标签: scala traits


【解决方案1】:

这是 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

【讨论】:

    【解决方案2】:

    问题是 trait 中的 val 在类中的 val 之前被初始化。

    因此,当您调用构造函数时,首先会初始化 area。它将其值设置为width * heightwidthheight 都没有被初始化,所以它们的值为零。这意味着area = 0 * 0 = 0

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

    它适用于lazy val,因为惰性 val 在访问时会检查它们是否已被初始化,如果没有则初始化它们自己。常规 val 没有此类检查。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多