2010-08-27 13 views
16
public class InterfaceCasting { 

    private static class A{} 

    public static void main(String[] args) { 
     A a = new A(); 
     Serializable serializable = new Serializable(){}; 
     a = (A)serializable; 
    } 

} 

Compilación tener éxito, pero en tiempo de ejecución excepcióninterfaz Java La conversión a Clase

Exception in thread "main" java.lang.ClassCastException: InterfaceCasting$1 cannot be cast to InterfaceCasting$A 

QUÉ COMPILACIÓN tener éxito? El compilador debe saber que serialiazable no es A?

+0

Creo que tiene que revisar los conceptos en Java Exception ... – ultrajohn

Respuesta

-1

Serializable NO es un A, por lo que arroja ClassCastException.

+3

Eso es correcto, pero no es lo que pidió el OP. – cherouvim

0

No se puede saber porque el tipo de tiempo de compilación serializable es Serializable.

Para ilustrar, considere esto:

private static class A{} 
private static class B implements Serializable {} 

Serializable serializable = new B(); 
A a = (A)serializable; 

esto es exactamente igual a su pregunta, que compila.

private static class A{} 
private static class B implements Serializable {} 

B b = new B(); 
A a = (A)b; 

esto no se compila porque b no es una A.

27

Como usted señala, esto hará de compilación:

interface MyInterface {} 

class A {} 

public class InterfaceCasting { 
    public static void main(String[] args) { 
     MyInterface myObject = new MyInterface() {}; 
     A a = (A) myObject; 
    } 
} 

Este sin embargo, se no de compilación:

interface MyInterface {} 

class A {} 

public class InterfaceCasting { 
    public static void main(String[] args) { 
     A a = (A) new MyInterface() {}; // javac says: "inconvertible types!" 
    } 
} 

Entonces, ¿qué está pasando aquí? ¿Cual es la diferencia?

Bueno, ya MyInterface es simplemente una interfaz, que podía muy bien ser implementada por una clase que extienda Una, en cuyo caso el CAST de MyInterface a A sería legal.


Este código, por ejemplo, se tener éxito en el 50% de todas las ejecuciones, e ilustra que el compilador tendría que resolver problemas indecidibles posiblemente con el fin de siempre "detectar" vaciados de ilegales en tiempo de compilación.

interface MyInterface {} 

class A {} 

class B extends A implements MyInterface {} 

public class InterfaceCasting { 
    public static void main(String[] args) { 
     MyInterface myObject = new MyInterface() {}; 
     if (java.lang.Math.random() > 0.5) 
      myObject = new B(); 
     A a = (A) myObject; 
    } 
} 
+0

¿Esto también es posible sin extender A? Porque eso es lo que necesito. – Ben

4
Serializable serializable; 
a = (A)serializable; 

Como para el compilador, el serializable variables puede contener cualquier objeto que implementa Serializable, que incluye las subclases de A. Por lo tanto, se supone que usted sabe que las variables efectivamente contienen un objeto A y permite esa línea.

+0

No, puede ** no ** incluir 'A'. El compilador sabe con certeza que 'A 'no implementa' Serializable'. – aioobe

+0

@aioobe sí, pero una subclase de 'A' puede –

+0

debe aclarar esto escribiendo" que incluye subclases de A ". – aioobe

1

El compilador no es lo suficientemente inteligente como para rastrear los orígenes de serializable y darse cuenta de que nunca puede ser del tipo A. En realidad, sólo evalúa la línea:

a = (A)serializable; 

y ve que serializable una referencia de tipo Serializable pero puede hacer referencia a una clase que también es de tipo A. La clase real que hace referencia a serializable no se conoce hasta el tiempo de ejecución.

En este caso trivial, sabemos que este modelo nunca tendrá éxito, pero en general esto queda como un problema de tiempo de ejecución ya que las diferentes rutas de código que pueden conducir a un lanzamiento son (en teoría) infinitas.

Si se quiere evitar este problema en tiempo de ejecución que podría poner a prueba para ello ..

if (serializable instanceof A) { 
    a = (A)serializable; 
} else .... 
0

Aunque no sé la respuesta correcta, no es generalmente una buena idea para echar una interfaz para una clase, por varias razones.

a) Una interfaz define un contrato, garantiza el comportamiento. Una clase puede definir más que este contrato, el uso de los otros métodos puede tener efectos secundarios inesperados y romper las API. P.ej. cuando se pasa una lista a un método y uno descubre que el objeto pasado es en realidad una Lista Enlazada, y la echas y usas los métodos basados ​​en la Cola que también define, estás rompiendo la API.

b) también, el objeto con la interfaz puede no ser un objeto "real" en tiempo de ejecución, pero quizás un proxy de servicio creado alrededor del objeto original por una biblioteca como Spring o EJB. Tu lanzamiento fallará en esos casos.

Si es absolutamente necesario fundido, no hacerlo sin una comprobación instanceof:

if(myServiceObject instanceof MyServiceObjectImpl){ 
    MyServiceObjectImpl impl = (MyServiceObjectImpl) myServiceObject; 
} 
0

Las modalidades de la legalidad en tiempo de compilación de una conversión de fundición de un valor de tiempo de compilación tipo de referencia S a un tipo de referencia de tiempo de compilación T es el siguiente:
[...]
Si S es un tipo de interfaz:
- Si T es un tipo de matriz, [...].
- Si T es un tipo que no es final (§8.1.1), entonces si existe un supertipo X de T, y un supertipo Y de S, de modo que tanto X como Y son tipos claramente parametrizados, y eso las borraduras de X e Y son las mismas, se produce un error en tiempo de compilación. De lo contrario, el lanzamiento siempre es legal en tiempo de compilación (porque incluso si T no implementa S, una subclase de T podría).

Fuente:
JLS : Conversions and Promotions

8

Java language specification estados, que:

Algunos moldes pueden ser incorrectas en tiempo de compilación; tales conversiones resultan en un error en tiempo de compilación.

Y más tarde en el programa Las modalidades de la legalidad en tiempo de compilación de una conversión de fundición de un valor de tiempo de compilación tipo de referencia S a un tipo de referencia de tiempo de compilación T - cuidado, que son muy complejos y difícil de entender

La regla es interesante:

  • Si S es una interfaz de Tipo:
    • Si T es un tipo que es no definitivo (§8.1.1) , entonces, si existe un supertipo X de T y un supertipo Y de S, de modo que tanto X como Y son tipos parametrizados claramente distintos, y que las borraduras de X e Y son las mismas, un error de tiempo de compilación o ccurs. De lo contrario, el lanzamiento siempre es legal en tiempo de compilación (porque incluso si T no implementa S, una subclase de T podría).

En su ejemplo, es perfectamente claro, que el reparto es ilegal. Pero considere esto ligero cambio:

public class InterfaceCasting { 

    private static class A{} 
    private static class B extends A implements Serializable{} 

    public static void main(String[] args) { 
     A a = new A(); 
     Serializable serializable = new B(){}; 
     a = (A)serializable; 
    }  
} 

Ahora un elenco de una Serializable a A es posible en tiempo de ejecución y esto demuestra, que en esos casos, es mejor dejar que el tiempo de ejecución para decidir si se puede emitir o no.

+0

sí, lo tengo ahora Gracias a todos por su ayuda – komenan

Cuestiones relacionadas