Аннотация Scala @throws игнорируется в javac, если я объявляю переменную как ее абстрактный суперкласс.

В Java вы не можете указать, что переопределенный абстрактный метод throws создает некоторое исключение, если исходный абстрактный метод этого не делает (overridden method does not throw Exception). Однако в Scala вы можете сделать это, поскольку в нем нет проверенных исключений. Хорошо, но если вы используете аннотацию @throws, это должно подсказать компилятору Java, что происходит, верно?

Учитывая этот код Scala:

package myscala

abstract class SFoo {
    def bar(): Unit
}

class SFoobar extends SFoo {
    @throws[Exception]
    override def bar(): Unit = {
        throw new Exception("hi there")
    }
}

У меня есть две разные Java-программы, одна из которых будет компилироваться и запускаться в Exception во время выполнения, а другая не будет компилироваться.

Компилирует:

import myscala.SFoo;
import myscala.SFoobar;

public class Foobar {
    public static void main(String[] args) {
        SFoo mySFoo = new SFoobar();
        mySFoo.bar();
    }
}

Не компилируется (unreported exception Exception; must be caught or declared to be thrown):

import myscala.SFoo;
import myscala.SFoobar;

public class Foobar {
    public static void main(String[] args) {
        SFoobar mySFoo = new SFoobar();       // only difference is the declared type
        mySFoo.bar();
    }
}

Я действительно не понимаю. Почему компилятор Java не улавливает тот факт, что я объявляю, что SFoobar.bar выдает исключение, даже если Foo.bar не имеет такого объявления, и в результате вызывает аналогичную ошибку компиляции?


person 2rs2ts    schedule 05.08.2014    source источник


Ответы (1)


Если вы преобразуете код Scala для SFoo и SFooBar в эквивалентный код Java:

abstract class SFoo {
    void bar() {}
}

class SFoobar extends SFoo {
    void bar() throws Exception {
        throw new Exception("hi there");
    }
}

Не компилируется, потому что:

error: bar() in SFoobar cannot override bar() in SFoo
        void bar() throws Exception {
             ^
  overridden method does not throw Exception

Java применяет правило, согласно которому переопределяющие методы не могут генерировать исключения, которые не были вызваны переопределяемым методом. Это делает проверенные исключения типобезопасными при восходящем преобразовании, например. при преобразовании SFooBar в SFoo.

Ваше первое определение класса FooBar, где SFooBar хранится в переменной SFoo, было бы совершенно безопасным, если бы SFoo и SFooBar были определены в Java. Компилятор Java знает, что SFoo.bar не генерирует Exception, предполагает, что правило для переопределенных методов было соблюдено, и поэтому не выполняет здесь никаких дополнительных проверок.

Однако компилятор Scala не заботится об этом правиле для переопределенных методов и позволит вам нарушить безопасность этого типа. Компилятор Java не обращает внимания на этот факт и поэтому создает код, который ломается во время выполнения. Это несоответствие между Java и Scala, которое ни один компилятор не может обойти.

person wingedsubmariner    schedule 05.08.2014