El operador 'instanceof' se comporta de manera diferente para interfaces y clases

88

Me gustaría saber sobre el siguiente comportamiento del instanceofoperador en 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
    }
}

¿Por que es esto entonces? No hay relación entre interface Cy class B, pero da falso mientras que en caso de obj instanceof Ada error de compilador

Ajay Sharma
fuente
12
Nota: si lo cambia a Object obj = new B(), se compila.
user253751
1
¿Qué te dice el error del compilador?
karfau
Si class Bes finalentonces obj instanceof Ctampoco se compilará, porque si no Bpuede tener subtipos, se garantiza que no está relacionado con C.
jaco0646

Respuestas:

127

Debido a que Java no tiene herencia de clases múltiples, se sabe absolutamente durante la compilación que el objobjeto de tipo Bno puede ser un subtipo A. Por otro lado, posiblemente puede ser un subtipo de interfaz C, por ejemplo en este caso:

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  
    }
}

Por lo tanto, mirar solo el obj instanceof Ccompilador de expresiones no puede decir de antemano si será verdadero o falso, pero al mirarlo obj instanceof Ase sabe que esto siempre es falso, por lo que no tiene sentido y le ayuda a evitar un error. Si aún desea tener esta verificación sin sentido en su programa, puede agregar una conversión explícita a Object:

System.out.println(((Object)obj) instanceof A);      //compiles fine
Tagir Valeev
fuente
1
El otro sabor de un cheque sin sentido es usarA.class.isAssignableFrom(obj.getClass())
David Ehrmann
Estoy un poco confundido con tu explicación, dijiste que Java has no multiple class inheritancesí, estoy de acuerdo, pero cómo se aplica en este caso porque ni B ni A extienden nada, así que ¿por qué herencias múltiples aquí. Sería útil si pudiera explicarlo?
codegasmer
@codegasmer Respuesta tardía: si Java permitía que una clase heredara de muchas otras clases, entonces uno podría hacer "la clase D extiende A, B" o algo así, y luego "B obj = new D ()", y hacer el "obj instanceof A "en la compilación de la pregunta original (mientras que actualmente no lo hace); de hecho, sería mejor compilar, porque se esperaría que se evaluara como verdadero. Pero si simplemente no se permite que nada sea tanto B como A, entonces la expresión "obj instancia de A" donde obj se define como tipo B puede considerarse razonablemente absurda.
mjwach
"Si aún desea tener esta verificación sin sentido", no tiene por qué ser insignificante, como si estas clases fueran de otra biblioteca / marco, existe la posibilidad de que esté utilizando una versión diferente en tiempo de ejecución donde B extends A. En mi vida, de hecho, necesitaba hacer comprobaciones y lanzamientos tan raros como (A)(Object)bsi en tiempo de ejecución fuera posible ser verdad.
GotoFinal
1

Al usar el finalmodificador en la declaración de clase a continuación, se garantiza que no puede haber una subclase de Testque pueda implementar la interfaz Foobar. En este caso, es obvio que Testy Foobarno son compatibles entre sí:

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 {
}

De lo contrario, si Testno se declara final, es posible que una subclase de Testimplemente la interfaz. Y es por eso que el compilador permitiría la declaración test instanceof Foobaren este caso.

Nurettin Armutcu
fuente