Оператор 'instanceof' поводиться по-різному щодо інтерфейсів та класів


88

Я хотів би знати про наступну поведінку instanceofоператора в Java.

interface C {}

class B {}

public class A {
    public static void main(String args[]) {
        B obj = new B();
        System.out.println(obj instanceof A);      //Gives compiler error
        System.out.println(obj instanceof C);      //Gives false as output
    }
}

Чому це так? Існує ніякого відношення між interface Cіclass B , але це видає помилкове, тоді як у випадку obj instanceof Aце видає помилку компілятора?


12
Примітка: якщо ви зміните його на Object obj = new B(), він компілюється.
user253751

1
Що говорить про помилку компілятора?
karfau

Якщо class Bє, finalтоді obj instanceof Cтакож не буде компілюватися, тому що якщо не Bможе мати підтипів, то воно гарантовано не буде пов'язане з C.
jaco0646

Відповіді:


127

Оскільки Java не має успадкування декількох класів, під час компіляції абсолютно відомо, що objоб'єкт типу Bне може бути підтипом A. З іншого боку, це може бути підтипом інтерфейсу C, наприклад, у цьому випадку:

interface C {}

class B {}

class D extends B implements C {}

public class A {
    public static void main(String args[]) {
        B obj = new D();
        System.out.println(obj instanceof C);      //compiles and gives true as output  
    }
}

Отже, дивлячись лише на obj instanceof Cкомпілятор виразів, не можна заздалегідь визначити, чи буде він істинним чи хибним, але, дивлячись на obj instanceof Aнього, відомо, що це завжди хибно, отже безглуздо і допомагає запобігти помилці. Якщо ви все ще хочете мати цю безглузду перевірку у своїй програмі, ви можете додати явний кастинг до Object:

System.out.println(((Object)obj) instanceof A);      //compiles fine

1
Іншим смаком безглуздої перевірки є використанняA.class.isAssignableFrom(obj.getClass())
David Ehrmann

Я трохи збентежений вашим поясненням, що ви сказали " Java has no multiple class inheritanceтак", я погоджуюсь, але як це застосовується в цьому випадку, оскільки ні B, ні A не поширюють нічого, то чому тут багаторазове успадкування. Було б корисно, якщо б ви могли пояснити?
codegasmer

@codegasmer Пізня відповідь: Якщо Java дозволила класу успадковуватись від кількох інших класів, тоді можна було б зробити "клас D розширює A, B" або якийсь такий, а потім "B obj = new D ()" і зробити "obj instanceof A "в оригінальній компіляції запитань (тоді як на даний момент це не так) - насправді йому було б краще скомпілювати, тому що, як очікується, оцінка має значення true. Але якщо просто не дозволяється, щоб щось було і B, і A, тоді вираз "obj instanceof A", де obj визначено як тип B, можна обґрунтовано вважати безглуздим.
mjwach

"Якщо ви все ще хочете мати цю безглузду перевірку" - вона не повинна бути безглуздою, так як якщо ці класи походять з іншої бібліотеки / фреймворку, то є ймовірність того, що ви будете використовувати іншу версію під час виконання де B extends A. У моєму житті мені насправді потрібно було робити такі дивні перевірки і касти, як, наприклад (A)(Object)b, під час виконання це насправді можна було бути правдою.
GotoFinal

1

Використовуючи finalмодифікатор у декларації класу нижче, гарантується, що не може бути підкласу Test, який може реалізувати інтерфейс Foobar. У цьому випадку очевидно, що Testі Foobarвони не сумісні між собою:

public final class Test {

    public static void main(String[] args) {
        Test test = new Test();
        System.out.println(test instanceof Foobar); // Compiler error: incompatible types
    }
}

interface Foobar {
}

В іншому випадку, якщо Testце не оголошено final, можливо, підклас Testреалізує інтерфейс. І тому компілятор дозволить висловлювання test instanceof Foobarв цьому випадку.

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