Лиття Java в інтерфейсах


75

Хтось може пояснити мені, як компілятор не скаржиться на першому кастингу, а скаржиться на другому?

interface I1 { }
interface I2 { }
class C1 implements I1 { }
class C2 implements I2 { }

public class Test{
     public static void main(){
        C1 o1 = new C1();
        C2 o2 = new C2();
        Integer o3 = new Integer(4);

        I2 x = (I2)o1; //compiler does not complain
        I2 y = (I2)o3; //compiler complains here !!
     }
}

8
Запустивши це, ви зіткнетеся ClassCastException, що є RuntimeException.
Buhake Sindi

4
@BuhakeSindi ти помиляєшся
Андремоній

2
@BuhakeSindi ОП заявив, що компілятор скаржився, пропонуючи мені, що ви не маєте можливості запустити це.
emory

@emory, навіть якби він міг змусити його скомпілювати, виправивши елемент, що мав помилку компілятора, рядок над ним викликав би ClassCastException.
Buhake Sindi

1
@BuhakeSindi Ви помиляєтесь, бо говорите те, що не слід. Вода мокра. Але неправильно говорити про це питання в цьому контексті. Гаразд? Очевидно, що допитувач знає про це, бо робить експеримент.
Валь

Відповіді:


148

Коли ви робите o1і o3з (I2), ви повідомляєте компілятору, що клас об'єкта насправді є підкласом свого оголошеного типу, і що цей підклас реалізує I2.

IntegerКлас остаточний , тому o3не може бути екземпляром підкласу Integer: компілятор знає , що ти брешеш. C1проте не є остаточним, тому o1 може бути екземпляром підтипу C1цього реалізатора I2.

Якщо ви зробите C1остаточний, компілятор теж скаржиться:

interface I1 { }
interface I2 { }
final class C1 implements I1 { }
class C2 implements I2 { }

public class Test{
     public static void main(){
        C1 o1 = new C1();
        C2 o2 = new C2();
        Integer o3 = new Integer(4);

        I2 y = (I2)o3; //compiler complains here !!
        I2 x = (I2)o1; //compiler complains too
     }
}

1
"Ціле число є остаточним, тому o3, оскільки немає шансів бути інтерфейсом I2" Це майже неправильно в цій формі. Не сенс, а фраза.
Powerslave

1
@WilQu Я скаржився не на помилку, а на формулювання відповіді. Якщо ви поглянете нижче, то побачите, наскільки чіткішими є фрази Етьєна про те саме. Ваш може бути misunderstandable, таким чином , вводить в оману , як ви не згадали (наприклад) , що клас з Integerє остаточним (що очевидно для мене , і вас , але , ймовірно , не всім) і «не має шансів бути інтерфейс» частина робить не нести занадто багато інформації; насправді інформація, про яку йдеться, прихована у другому реченні. Можна навіть подумати, що finalкласи не можуть реалізовувати поглинання, що не відповідає дійсності.
Powerslave

@Powerslave дякую за ваші коментарі, я знову відредагував свою відповідь. Я намагався додати більше пояснень, сподіваюся, це не зробить мою відповідь ще більш заплутаною.
WilQu

@WilQu Відмінно! Виявилося дуже гарне пояснення :)
Powerslave

35

Відповідно до глави 5 JLS

5.5.1. Кастинг еталонного типу

З огляду на тип посилання під час компіляції S (джерело) та посилальний тип компіляції T (цільовий), існує перетворення кастингу з S на T, якщо помилки під час компіляції не виникають через наступні правила. Якщо T - тип інтерфейсу:

Якщо S не є кінцевим класом (§8.1.1), то, якщо існує надтип X від T і надтип Y від S, такі що X і Y є доказово різними параметризованими типами, і що стирання X і Y однакові, виникає помилка під час компіляції.

В іншому випадку акторський склад завжди легальний під час компіляції (оскільки навіть якщо S не реалізує T, підклас S може бути).

Якщо S є кінцевим класом (§8.1.1), тоді S повинен реалізувати T, або виникає помилка під час компіляції.


23

Це тому, що клас Integerє остаточним і C1ні. Таким чином, об’єкт Integer не може реалізувати I2, тоді як об’єкт C1 може, якщо це екземпляр підкласу C1, який реалізує I2.


15

Відповідно до JLS 5.5.1 - Відливання за типовим типом застосовуються правила:

  • Якщо T - тип класу, то або | S | <: | T |, або | T | <: | S |. В іншому випадку виникає помилка під час компіляції.

    I2 y = (I2)o3; //compiler complains here !!

У цьому випадку а Integerта I2ніяк не пов'язані між собою , тому виникає помилка під час компіляції. Крім того, оскільки Integerє final, немає зв'язку між Integerі I2.

I2і I1можуть бути пов’язані через те, що обидва вони є інтерфейсом маркера (контракту немає).

Що стосується складеного коду, правило дотримується:

  • Якщо S не є кінцевим класом (§8.1.1), то, якщо існує надтип X від T і надтип Y від S, такі що X і Y є доказово різними параметризованими типами, і що стирання X і Y однакові, виникає помилка під час компіляції.

Sє o1і Tє I2.

Сподіваюся, це допомагає.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.