Si trato de enviar un String a java.util.Date , el compilador de Java detecta el error. Entonces, ¿por qué el compilador no marca lo siguiente como un error?
List<String> strList = new ArrayList<>(); Date d = (Date) strList; Por supuesto, la JVM lanza una ClassCastException en tiempo de ejecución, pero el compilador no la marca.
El comportamiento es el mismo con javac 1.8.0_212 y 11.0.2.
El elenco es técnicamente posible. Javac no puede probar fácilmente que no es así en su caso y el JLS en realidad define esto como un programa Java válido, por lo que marcar un error sería incorrecto.
Esto se debe a que List es una interfaz. Por lo tanto, podría tener una subclase de una Date que realmente implemente List disfrazada de List aquí, y luego convertirla en Date estaría perfectamente bien. Por ejemplo:
public class SneakyListDate extends Date implements List<Foo> { ... }Y luego:
List<Foo> list = new SneakyListDate(); Date date = (Date) list; // This one is valid, compiles and runs just fineEs posible que no siempre sea posible detectar un escenario de este tipo, ya que requeriría información de tiempo de ejecución si la instancia proviene, por ejemplo, de un método. E incluso si, requeriría mucho más esfuerzo para el compilador. El compilador solo evita conversiones que son absolutamente imposibles debido a que no hay forma de que el árbol de clases coincida en absoluto. Que no es el caso aquí, como se ve.
Tenga en cuenta que el JLS requiere que su código sea un programa Java válido. En 5.1.6.1. Conversión de referencia de reducción permitida dice:
Existe una conversión de referencia de restricción del tipo de referencia
Sal tipo de referenciaTsi todo lo siguiente es verdadero :
- [...]
- Se aplica uno de los siguientes casos:
- [...]
Ses un tipo de interfaz,Tes un tipo de clase yTno nombra una clasefinal.
Entonces, incluso si el compilador pudiera darse cuenta de que su caso es realmente imposible, no se le permite marcar un error porque JLS lo define como un programa Java válido.
Solo se permitiría mostrar una advertencia.
Consideremos una generalización de su ejemplo:
List<String> strList = someMethod(); Date d = (Date) strList; Estas son las principales razones por las que Date d = (Date) strList; no es un error de compilación.
La razón intuitiva es que el compilador (en general) no conoce el tipo preciso del objeto devuelto por esa llamada al método. Es posible que además de ser una clase que implementa List , también sea una subclase de Date .
La razón técnica es que la especificación del lenguaje Java "permite" la conversión de referencia de restricción que corresponde a este tipo de conversión. Según JLS 5.1.6.1 :
"Existe una conversión de referencia de restricción del tipo de referencia
Sal tipo de referenciaTsi todo lo siguiente es verdadero:"...
5) "
Ses un tipo de interfaz,Tes un tipo de clase yTno nombra una clasefinal"....
En un lugar diferente, JLS también dice que se puede lanzar una excepción en tiempo de ejecución...
Tenga en cuenta que la determinación de JLS 5.1.6.1 se basa únicamente en los tipos declarados de las variables involucradas en lugar de los tipos de tiempo de ejecución reales. En el caso general, el compilador no conoce ni puede conocer los tipos reales de tiempo de ejecución.
Entonces, ¿por qué el compilador de Java no puede determinar que la conversión no funcionará?
En mi ejemplo, la llamada a someMethod podría devolver objetos con una variedad de tipos. Incluso si el compilador pudiera analizar el cuerpo del método y determinar el conjunto preciso de tipos que podrían devolverse, no hay nada que impida que alguien lo cambie para devolver diferentes tipos... después de compilar el código que lo llama. Esta es la razón básica por la que JLS 5.1.6.1 dice lo que dice.
En su ejemplo, un compilador inteligente podría darse cuenta de que el elenco nunca puede tener éxito. Y está permitido emitir una advertencia en tiempo de compilación para señalar el problema.
Entonces, ¿por qué no se le permite a un compilador inteligente decir que esto es un error de todos modos?
Porque el JLS dice que este es un programa válido. Período. Cualquier compilador que llamara a esto un error no sería compatible con Java.
Además, cualquier compilador que rechace programas Java que JLS y otros compiladores digan que son válidos, es un impedimento para la portabilidad del código fuente de Java.
5.5.1. Tipo de referencia Fundición:
Dado un tipo de referencia en tiempo de compilación
S(fuente) y un tipo de referencia en tiempo de compilaciónT(destino), existe una conversión de conversión deSaTsi no se producen errores en tiempo de compilación debido a las siguientes reglas.[...]
Si
Ses un tipo de interfaz:
[...]
Si
Tes una clase o un tipo de interfaz que no es final, entonces si existe un supertipoXdeTy un supertipoYdeS, de modo que tantoXcomoYsean tipos parametrizados distintos y que los borrados deXeYson iguales, se produce un error en tiempo de compilación.De lo contrario, la conversión siempre es legal en tiempo de compilación (porque incluso si
Tno implementaS, una subclase deTpodría).
List<String> es S y Date es T en su caso.