Абстрактный метод Scala является нулевым в суперклассе, когда подкласс реализует его с помощью val? - PullRequest
5 голосов
/ 28 июля 2010

Я нашел ошибку в моем скала-коде, которая меня озадачивает. Ниже приведен упрощенный вариант проблемы.

В конструкторе абстрактного класса я хочу проверить несколько утверждений об абстрактных методах. Таким образом, когда создается объект подкласса, эти утверждения проверяются, чтобы убедиться, что все реализовано так, как должно.

Плохо, когда подкласс реализует абстрактный метод, используя «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 реализует aval, используя «val», а B - «def».

Так что мой вопрос, больше всего, почему эта разница? Я не понимаю, что происходит.

И есть ли способ заставить его работать так, как я хочу, в обоих случаях в scala? Или мне просто не хватает более элегантного способа отстаивать то, что я хочу в scala?

Ответы [ 2 ]

6 голосов
/ 28 июля 2010

В Scala вы можете использовать функцию ранних определений для инициализации val подкласса перед вызовом супер-конструктора:

class C extends {
  val aval = "B"
} with A
5 голосов
/ 28 июля 2010

Этот эквивалентный 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

такой проблемы нет.

Обычно следует избегать вызова виртуальных методов из конструктора.

И есть ли способ заставить его работать так, как я хочу, в обоих случаях в scala?

См. этот вопрос для некоторых подходов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...