Оператор 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, но он дает false, тогда как в случае obj instanceof Aон дает ошибку компилятора?

Аджай Шарма
источник
12
Примечание: если вы измените его на Object obj = new B(), он компилируется.
user253751
1
Что вам сообщает Ошибка компилятора?
karfau
Если class Bэто, finalто obj instanceof Cтоже не будет компилироваться, потому что, если Bу него не может быть подтипов, то он гарантированно не связан с C.
jaco0646 05

Ответы:

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())
Дэвид Эрманн,
Я немного смущен вашим объяснением, которое вы сказали, Java has no multiple class inheritanceда, я согласен, но как оно применяется в этом случае, потому что ни B, ни A не расширяют ничего, так почему здесь множественное наследование. Было бы полезно, если бы вы могли объяснить?
codegasmer
@codegasmer Поздний ответ: если Java позволяла классу наследовать от нескольких других классов, тогда можно было бы сделать «class D extends 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в этом случае компилятор разрешит оператор .

Нуреттин Армутку
источник