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

Posted

技术标签:

【中文标题】当子类使用val实现它时,超类中的Scala抽象方法为null?【英文标题】:Scala abstract method is null in superclass when subclass implements it using val? 【发布时间】: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 中断言我想要的东西?

【问题讨论】:

【参考方案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 代码中一样,它更清楚发生了什么。在我的特定情况下,我将寻找另一种优雅地检查子类的方法,您的链接可能会有所帮助。至少这个错误的奥秘现在已经消失了;-)

以上是关于当子类使用val实现它时,超类中的Scala抽象方法为null?的主要内容,如果未能解决你的问题,请参考以下文章

什么时候需要显式调用超类构造函数?

在抽象超类的静态方法中创建子类的实例? [复制]

23种设计模式之策略设计模式

设计模式之模板方法模式

Scala 基础 :面向对象(下篇)

设计模式——模板模式方法