我在scala代码中发现了一个错误,这让我很困惑。以下是问题的简化版本。
在抽象类的构造函数中,我想检查一些关于抽象方法的断言。 因此,当创建子类的对象时,将检查这些断言,以查看是否所有这些断言都应该实现。
当子类使用“val”实现抽象方法时出错:
Scala代码:
abstract class A {
def aval : String
assert(aval != null, "aval == null")
assert(aval == "B", "aval: "+aval)
}
class B extends A {
def aval = "B"
}
class C extends A {
val aval = "B"
}
object VariousScalaTests {
def main(args : Array[String]) : Unit = {
val b = new B
val c = new C
}
}
Scala错误:
Exception in thread "main" java.lang.AssertionError: assertion failed: aval == null
at scala.Predef$.assert(Predef.scala:92)
at A.<init>(VariousScalaTests.scala:4)
at C.<init>(VariousScalaTests.scala:12)
at VariousScalaTests$.main(VariousScalaTests.scala:19)
at VariousScalaTests.main(VariousScalaTests.scala)
所以它在最后一行代码中失败了:“val c = new C”。 B级工作得很好,但C级没有!唯一的区别是C使用“val”实现aval,使用“def”实现B.
所以我的问题,最重要的是,为什么会出现这种差异?我不明白发生了什么。
有没有办法让它在scala中的两种情况下都能正常工作?或者我只是错过了一种更优雅的方式来在scala中断言我想要的东西?
答案 0 :(得分:6)
在Scala中,您可以使用早期定义功能在调用超级构造函数之前初始化子类的val:
class C extends {
val aval = "B"
} with A
答案 1 :(得分:5)
这个等效的Java代码应该解释这个问题:
public abstract class A {
public String aval();
}
public class B extends A {
public String aval() {
return "B";
}
}
public class C extends A {
private String _aval;
public C() {
_aval = "B";
}
public String aval() {
return _aval;
}
}
运行时
val c = new C
A
的构造函数在C
的构造函数1}之前运行并且尚未分配_aval
字段。因此aval()
方法返回null
(_aval
字段的初始值)。但是在
val b = new B
没有这样的问题。
通常,you should try to avoid calling virtual methods from a constructor.
有没有办法让它在scala的两种情况下都能正常工作?
有些方法,请参阅this question。