Inheritance 当子类使用val实现Scala抽象方法时,它在超类中为null?

Inheritance 当子类使用val实现Scala抽象方法时,它在超类中为null?,inheritance,scala,constructor,Inheritance,Scala,Constructor,我在scala代码中发现了一个错误,这让我很困惑。下面是问题的简化版本 在抽象类的构造函数中,我想检查一些关于抽象方法的断言。 因此,当创建子类的对象时,将检查这些断言,以查看是否所有的断言都按其应该的方式实现 当子类使用“val”实现抽象方法时出错,但是: Scala代码: abstract class A { def aval : String assert(aval != null, "aval == null") assert(aval == "B", "aval

我在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)
线程“main”java.lang.AssertionError中出现异常:断言失败:aval==null 在scala.Predef$.assert处(Predef.scala:92) 在A点(各种比例测试。比例:4) 在C点(各种比例测试,比例:12) 在VariousScalaTests$.main(VariousScalaTests.scala:19) 在VariousScalaTests.main(VariousScalaTests.scala)处 因此,它在代码的最后一行失败:“val c=new c”。 B级工作得很好,但C级不行!唯一的区别是C使用“val”实现aval,B使用“def”

所以我的问题,最重要的是,为什么会有这种差异?我不明白发生了什么事


在scala中,有没有一种方法可以让它在这两种情况下都像我所希望的那样工作?或者我只是缺少一种更优雅的方式来断言我在scala中想要什么?

这段等价的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
的构造函数之前运行,
\u aval
字段尚未分配。因此
aval()
方法返回
null
\u aval
字段的初始值)。但是在

没有这样的问题

一般来说

在scala中,有没有一种方法可以让它在这两种情况下都像我所希望的那样工作


有关一些方法,请参阅。

在Scala中,您可以使用早期定义功能在调用超级构造函数之前初始化子类的val:

class C extends {
  val aval = "B"
} with A

您的java代码解释得很好!我现在明白了。。。考虑到这样的行为,“避免从构造函数调用虚拟方法”似乎是一个非常好的建议。当然,在scala中,正如在java代码中一样,发生了什么更清楚。在我的具体案例中,我将搜索另一种方式来优雅地检查子类,您的链接可能会有所帮助。至少这个错误的神秘性现在已经消失了;-)不确定。在scala lang规范第5.1.6节中,它被称为“早期定义”。
class C extends {
  val aval = "B"
} with A