Bueno, la motivación (compatibilidad con versiones anteriores) es tanto una ventaja como una desventaja. Es una desventaja porque todos preferiríamos tener tipos reificables, pero el precio a pagar era alto. Considere las opciones de diseño en C #. Tienen tipos reificables, pero ahora tienen API duplicadas. Entonces, imagine una API Java donde también tenemos API duplicadas para cada clase parametrizada. Ahora, imagínese portando miles de líneas de código de clases heredadas a las nuevas clases genéricas. Ahora, ¿quién no consideraría una API duplicada una desventaja? Pero bueno, ¡tienen tipos reificables!
Entonces, la principal motivación fue "evolución, no revolución". Y lógicamente, cada decisión tiene compensaciones.
Además de las otras desventajas mencionadas, también podríamos agregar el hecho de que la eliminación de tipos puede ser difícil de razonar en el momento de la compilación, ya que no es evidente que se eliminen ciertos tipos, y esto conduce a errores muy extraños y difíciles de encontrar.
La existencia de métodos puente (este compilador genera métodos sintácticamente para mantener la compatibilidad binaria) también puede verse como una desventaja. Y estos pueden ser precisamente uno de los motivos de los errores que mencioné en el párrafo anterior.
Las principales desventajas se derivan del hecho ya obvio de que hay una sola clase y no múltiples clases para los tipos genéricos. Como otro ejemplo, considere que sobrecargar un método con la misma clase genérica falla en Java:
public void doSomething(List<One>);
public void doSomething(List<Two>);
Algo que podría verse como una desventaja de los tipos reificables (al menos en C #) es el hecho de que causan la explosión del código . Por ejemplo, List<int>
es una clase, y a List<double>
es otra totalmente diferente, ya que es a List<string>
y a List<MyType>
. Por lo tanto, las clases deben definirse en tiempo de ejecución, lo que provoca una explosión de clases y consume recursos valiosos mientras se generan.
Con respecto al hecho de que no es posible definir un new T()
en Java, mencionado en otra respuesta, también es interesante considerar que esto no es solo una cuestión de borrado de tipo. También requiere la existencia de un constructor predeterminado, por eso C # requiere una "nueva restricción" para esto. (Consulte Por qué el nuevo T () no es posible en Java , por Alex Buckley).
T
s, solo se obtiene una copia delClass<T>
código para todos losT
s; más una copia adicional para cada tipo de valorT
realmente utilizado.La desventaja de la eliminación de tipo es que no conoce en tiempo de ejecución el tipo de genérico. Esto implica que no puede aplicar una reflexión sobre ellos y que no puede crear instancias en tiempo de ejecución.
No puedes hacer algo así en Java:
Hay una solución para esto, pero requiere más código. La ventaja, como lo ha mencionado, es la compatibilidad con versiones anteriores.
fuente
Otra ventaja de los genéricos borrados es que los diferentes lenguajes que se compilan con la JVM emplean diferentes estrategias para los genéricos, por ejemplo, la covarianza del sitio de uso de Scala frente al sitio de uso de Java. Además, los genéricos de clase superior de Scala habrían sido más difíciles de soportar en los tipos reificados de .Net, porque esencialmente Scala en .Net ignoró el formato de reificación incompatible de C #. Si tuviéramos genéricos reificados en la JVM, lo más probable es que esos genéricos reificados no sean adecuados para las características que realmente nos gustan de Scala, y estaríamos atrapados con algo subóptimo. Citando del blog de Ola Bini ,
Personalmente, no considero la necesidad de usar un
TypeTag
contexto vinculado en Scala para métodos sobrecargados conflictivos como una desventaja, porque mueve la sobrecarga y la inflexibilidad de la reificación de un global (programa completo y todos los idiomas posibles) a un sitio de uso por idioma problema que es solo el caso con menos frecuencia.fuente