Почему класс может приводить несвязанный класс только с ошибкой времени выполнения, но не с ошибкой компилятора - PullRequest
0 голосов
/ 12 декабря 2018

Вот мой код:

public class A {
    int size = 100;
    public int getSize() {
        return size;
    }
    interface D {

    }
    class B implements D {

    }
    class C {
        int size = 999;
        public int getSize() {
            return size;
        }
    }
    public void test() {
        D d = new B();
        System.out.println(((C) d).getSize());
    }
    public static void main(String[] args) {
        A a = new A();
        a.test();
    }
}

Код компилируется без каких-либо ошибок компилятора.Почему бы не иметь ошибку компилятора.Класс C не имеет никакого отношения к классу ссылочного типа D и фактическому типу класса B. Как он прошел проверку компилятором на приведение типа?

Ответы [ 2 ]

0 голосов
/ 12 декабря 2018

Из JLS 5.5.1, приведение типа ссылки: https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.5.1

При заданном типе ссылки времени компиляции S (источник) и типе ссылки времени компиляции T (цель), приведение типасуществует преобразование из S в T, если из-за следующих правил не возникает ошибок времени компиляции.

В нашем случае S = D (интерфейс), T = C (класс).

Если S является типом класса:

S - это интерфейс, поэтому мы пропускаем это.

Если S - это тип интерфейса:

D - это интерфейс, поэтому мы используем эту ветвь.

Если T является типом массива, то S должен иметь тип java.io.Serializable или Cloneable (единственные интерфейсы, реализуемые массивами), иначе возникает ошибка времени компиляции.

T не является типом массива, мы пропускаем это.

Если T является классом или интерфейсным типом, который не является окончательным (§8.1.1), то если существует супертип XT и супертип Y для S, такой, что X и Y являются доказуемо различными параметризованными типами и что стирание X и Y одинаково, возникает ошибка времени компиляции.

Мынет этого.Мы, конечно, можем создать его, чтобы это произошло, но для этого требуется переопределение C.

В противном случае приведение всегда допустимо во время компиляции (потому что даже если Tне реализует S, подкласс T может).

Другими словами, у компилятора нет причин выдавать ошибку, поскольку вполне возможно, что в другом месте существует возможный класскоторый может использоваться для квалификации приведения.

Например, вы могли бы иметь это в другом месте:

class E extends C implements D { ... }

Завершение C вызывает ошибку времени компиляции:

final class C { ... }

По существу, существует вероятность того, что существует реализация C, которая может работать во время выполнения, что невозможно проверить во время компиляции.

0 голосов
/ 12 декабря 2018

Вот мое человеческое объяснение, см. Ответ @Compass для деталей спецификации.

Причина в том, что вы выполняете приведение из интерфейса, где фактический класс экземпляра, который он содержит, может соответствоватькласс, к которому вы обращаетесь .d может содержать и экземпляр типа C.

Только тип переменной имеет значение для времени компиляции, в то время как фактический тип экземпляра имеет значение для времени выполнения.

См. Этот код дляпример:

class B { ... }
class C { ... }
interface D { ... }

// only variable type matters for compile-time
// the compiler (usually?) doesn't care what's in them
D d = ...;
B b = ...;

// compile error
// `b` can't contain an instance inheriting from `C`.
C c2 = (C) b;

// no compile error
// `d` might contain an instance inheriting from `C`.
C c1 = (C) d;


// it's because of the possibility like below
// that the compiler doesn't complain.
class E extends C implements D { ... }

D d = new E();
C c = (C) d;
...