【问题标题】:Scala abstract method is null in superclass when subclass implements it using val?当子类使用val实现它时,超类中的Scala抽象方法为null?
【发布时间】:2010-07-28 13:13:14
【问题描述】:

我在我的 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,B使用“def”实现。

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

有没有办法让它在 scala 的两种情况下都能按我的意愿工作?还是我只是错过了一种更优雅的方式来在 scala 中断言我想要的东西?

【问题讨论】:

    标签: inheritance scala constructor


    【解决方案1】:

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

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

    【讨论】:

    • 不确定。在 scala lang 规范第 5.1.6 节中,它被称为“早期定义”。
    【解决方案2】:

    这个等效的 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 的构造函数之前,_aval 字段尚未分配。所以aval()方法返回null_aval字段的初始值)。但是在

    val b = new B
    

    不存在这样的问题。

    一般情况下,you should try to avoid calling virtual methods from a constructor.

    有没有办法让它在 scala 的两种情况下都按我的意愿工作?

    有关一些方法,请参阅this question

    【讨论】:

    • 你的java代码解释得很好!我现在明白了......考虑到这样的行为,“避免从构造函数调用虚拟方法”似乎是非常好的建议。当然在 scala 中,就像在 java 代码中一样,它更清楚发生了什么。在我的特定情况下,我将寻找另一种优雅地检查子类的方法,您的链接可能会有所帮助。至少这个错误的奥秘现在已经消失了;-)
    猜你喜欢
    • 2012-03-05
    • 2018-12-27
    • 1970-01-01
    • 1970-01-01
    • 2016-12-28
    • 2013-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多