Diablos! ¿que podemos hacer con los generics?!

Este más que una entrada para compartir algo es un post para quejarme.

Me gusta muchísimo la forma en la que usar generics me evita tener que usar el cast al sacar datos de las colecciones y con el autoboxing a veces creo que es demasiado bueno.

Pero hay escenarios en los que es demasiado malo, especialmente cuando el tipo de dato generico es uno que a su vez es generico.

Por ejemplo el siguiente código:

 
el que el formateador no lo muestre bien, prueba mi punto

Lo que hace el código no es importante en si mismo, simplemente transforma una lista en un mapa, el problema es que la declaración del método es ridículamente larga incluso para Java,

Antes del generics sería como:

 

Claro, el problema se mueve cuando se intenta usar el contenido porque hay que andar tirando cast por todos lados, eso ya lo vivimos en la era pre-1.5 y no queremos volver ahí, pero no habría otra manera?

En Java 1.7 ya será posible usar el "operador" diamante, pero no parece ser suficiente:

 

Como lo hacen otros lenguajes? C# por ejemplo? , C++? Scala? alguién sabe?

Desde que encontré SML me parece genial su inferencia, pero a veces pienso que demasiada magia hace daño.

Bueno, ya me quejé...

:)

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de ezamudio

usuarios, no programadores

El ejemplo que estás poniendo al principio es como de código que haces en una biblioteca o algo así, para implementar una API que otros van a usar. Cuando haces eso eres como un programador que hace software para programadores; otros programadores son tus usuarios.

Lo de generics está orientado a facilitarle la vida a los programadores usuarios, los que usan APIs, no los que las hacen.

Pero... estoy muy de acuerdo es que puede ser absurdamente largo. No entendí bien por qué decidieron implementar los generics como lo hicieron; eso de tener que poner el maldito   como que no viene al caso; la neta es que deberías poder poner simplemente   y con eso deberías esperar instancias de Padre o de cualquier clase que extienda Padre. Digo, la herencia sigue aplicando, no? Pues no, en generics no... si pones   solamente puedes tener instancias de Padre, y resulta que no puedes meter instancias de clases que extienden Padre. No tiene ningún sentido.

Imagen de rugi

Je.... seguramente quièn lo

Je.... seguramente quièn lo implementò tenía algún complejo de "quedar bien con todos".

Justo describes el escenario de "utilidad", que ocurre si (si y solo si) quieres esperar únicamente instancias de la clase Padre y no de las que lo extienden (o a la inversa), pues, se define justamente un mecanismo de discriminación.

Lo interesante (y lo que le daría cierta validez a esta teoría) es encontrar escenarios de este tipo.

Saludos!!

Muy cierto lo de Generics

Muy cierto lo que menciona Oscar. La verdad es que si se puede hacer algo como agregar un dato a un generico, osea esto:

 

siempe y cuando :
 
   

Recuerdan cuando postearon el lanzamiento de Ceylon donde @bferro nos mencionaba lo de "covariante, contravariante" que yo mencionaba que lo conocia como como UpCasting y DownCasting que tambien Enrique nos anticipaba que en java no se podia hacer justamente esto... pues la excepcion no es para los generics donde no puedes hacer por ejemplo:
 
aunque si puedas hacer:
 

   
Sea como sea, no se que tanta lógica para java sea el no soportar esto (como dice Enrique "No entendí bien por qué decidieron implementar los generics como lo hicieron")
  
Por cierto tuve que leer como 5 veces tu codigo para entenderle, si que es poco comun ver algo asi

Imagen de ezamudio

La diferencia

La cosa con el extends es que permite más flexibilidad en asignaciones cuando hay generics más específicos... es más fácil explicarlo con código:

 

Wildcard

Me queda una duda, tengo entendido que no se puede utilizar el wildcard al momento de hacer la instanciación del objeto al cual apuntará la referencia, ejemplo:

 

Solo se puede utilizar el wildcard al declarar la referencia.

La duda me surgió por que Oscar lo usa en su ejemplo

 

Imagen de ezamudio

indirección

El código de Oscar compila porque el wildcard se lo aplica al Diagnostic, no al mapa externo ni a la lista que se define como valor en el mapa.

Imagen de bferro

Las listas y las otras colecciones en Java son invariantes

Lo que molesta a Enrique (@ezamudio). Repito aquí el código que escribe Enrique:

 

Tendrás que aceptar esa molestia. Java no permite especificar la covarianza ni la contravarianza en el momento de la definición. Solo lo puede hacer de forma limitada en el momento de la llamada con el uso de comodines como lo escribes en tu código.
Las listas genéricas son invariantes con respecto al argumento tipo con el que las parametrizas.
  están en el mismo nivel de la jerarquía, aunque   sea un subtipo de  .

Por herencia...

Creo que la forma correcta de leer   es que estas declarando explicitamente que aceptas CUALQUIER tipo de objeto que herede de Number... asi ya cobra mucho sentido usar el wildcard PERO NO DEBERIA SER NECESARIO como anteriormente se comentaba.

estas declaraciones que tienen que ser explicitas me imagino que es para que semanticamente solo uedan hacer downcast y no upcast porque al hacer   ya no serviria este codigo

Imagen de bferro

El hubiera no existe

La covarianza, la contravarianza pueden ser tan útiles como la invarianza.
Java decide no aplicar la covarianza en el sitio de definición de una colección genérica. Scala permite que en el sitio de definición puedas determinar que la colección es invariante  , que es covariante   o que es contravariante  .

Un ejemplo mundano de uso de una colección invariante. Vas a un restaurant argentino y pides una parrillada (una colección de cárnicos). El camarero ama la covarianza y te sirve en la mesa una parrillada de puro chorizo. El tipo es programador y entre su reglas está que un chorizo es un tipo de cárnico y por consiguiente aplica la herencia siguiendo su pensamiento covariante.
Seguramente te vas a enojar.

Imagen de ezamudio

tupla

Una parrillada es más como una tupla que una colección simple con Generics, porque está muy claramente especificado lo que incluye. Ningún menu la define como "carnes varias", siempre dice cuáles trae (arrachera, pollo, chorizo, costilla, etc).

Imagen de bferro

Totalmente de acuerdo y esa es la razón de uso de la invarianza

Esperaba esa respuesta, porque precisamente esa es la razón de uso de una colección genérica invariante, cuando impones esa restricción en el diseño donde no permites la covarianza.
Otro ejemplo mundano es el de una cesta de frutas y lo que te sirven es un cesta de puras naranjas.

Eso no quiere decir que la covarianza se necesita para otros escenarios. Lo que falta en Java es lo que añaden otros lenguajes como Scala, de darle al programador la posibilidad de decidir eso en el momento de la definición de la colección, incluyendo el caso invariante.
C#, así como otros lenguajes para el CLR tampoco soportan o soportaban la covarianza. En el caso de C# lo soporta a partir de la versión 4.0 para interfaces y delegados.

Imagen de bferro

Curiosamente las tuplas en Scala sí son covariantes

Las tuplas en Scala sí son covariantes, de forma que el siguiente código es válido:
 

Si no se parametriza correctamente la tupla, pueden servirte una parrillada de puro chorizo. El lenguaje requiere que las restricciones de diseño estén claras.

Imagen de Nopalin

Diseño

Pues igual tienen razón en lo que todos comentan de los generics en java, pero en lo personal creo que lo que comenta oscar no es el problema correcto para la realización de una app, si no el quie diseño un metodo con tantos generics?

Aunque ya si se esta tratando de testear el lenguage ps si, entonces si hay bronca.

Por otro lado, java no te forza a que la implementación tenga generics, tu puedes hacer esto:

 

a final de cuentas lo que vas a utilizar es la variable con la declaración de su tipo, no la implementación, ya que como muchos han dicho, los generics no se checan en tiempo de ejecución si no de compilación.

sobres

@nopalin Hice lo que

@nopalin Hice lo que sugieres pero sale esto mira:

 

También es posible usar colecciones no generificadas (¿?) pero habría que volver a cast-landia y mejor no.

El ejemplo que puse es un caso de la vida real donde estoy usando un API de Java ( Diagnostic )

Al final lo terminé "resolviendo" ( aunque no había nada que resolver ) creando un tipo de dato que se pudiera usar en vez de toda la declaración esa.

Creé una clase privada:
 

Y con eso sustituí:
 

Por:

 

Lo cual mejora la legibilidad y por otro lado refuerza la semántica del código ( es decir, dice mejor que quiero hacer )

Imagen de ezamudio

WTF?

A ver, me perdí de algo, ya están usando Java 8, o en Ryz se puede definir una List con 2 tipos genéricos?

 

Imagen de bferro

WTF?

Eso digo yo Enrique

Imagen de Nopalin

equivocacion

jajaja chale, en realidad si hize un errorsote por no probar el codigo, en ralidad fue algo que puse asi a la rápida para demostrar el punto de que la implementación no debe llevar generics obligadamente, solo la declaración de tipo de la variable, a ver ahora si un ejemplo que compila:

 

aunque como dije en el comentario anterior, quien haga una declaración de tipo con tantos generics tiene un mal diseño, lo mejor como hizo oscar es hacer un objeto que englobe los datos que quiere manipular, aunque bueno eso ya es cuestion de la forma de ver de cada uno.

saludos

Ahhhhh seeeeehh.... ¬¬ por

Ahhhhh seeeeehh.... ¬¬ por eso digo que NO compila :P

Ahora si aplica mi segundo comentario, se puede hacer pero hay que volver a castinglandia.

 

Ese warning: [unchecked] unchecked conversion es como tirar a la basura el chequeo y se soportó para ser retro-compatibles.

Por cierto... si fueran a

Por cierto... si fueran a diseñar un nuevo lenguaje, como harían los generics :) :) :)

Lo que yo tengo pensado es soportar la covarianza (¿o era contravarianza?) por default, que sería también para mí un tanto más natural.

Así lo anterior sería:

 

Aunque bueeeno eso lo tengo muy lejos en mi roadmap e incluso se podría considerar acaso un tema avanzado ( espero que todos lo consideren así y no lo sea solo porque yo no domino el tema )

La alternativa es no usar generics at all e inferir el tipo de dato cuando sea posible y cuando no decir: "El compilador no pudo inferir el tipo..." y forzar a usar casting.

 

Pero claro, hay problemas serios cuando se usa como parametro:

 

El dilema estaría en saber como declarar "aList" si un   , un   o un  

En fin, aun falta mucho para eso, a ver si para entonces ya entendí bien los generics.

Imagen de rojovizcaino

+1

Si la pregunta es ¿qué podemos hacer? pues como dice Nopalin mejora el diseño encapsulando las estructuras de datos en objetos creo es la mejor respuesta. Sobre todo cuando se trata de parametros o retornos de métodos públicos. ¿Cuántas veces no han visto repetido un ciclo for sobre una lista donde se busca el elemento con la propiedad tal que cumpla la condición cual regado por todas partes? Si la estructura se encuentra encapsulada ese código queda en un sólo lugar y todo mundo es más feliz.

Objetos

Yo pienso igual que java: si no defines e tipo que sea de objetos, por que? Pues porque todos vienen de Object y blah blah blah cosa que por default puedes convertir lo que sea a Object... de otra manera si quieres hacerlo mas inteligente tendrias que introspeccionar (uuuf que palabra!) tu objeto lista para ver si puedes llegar a una clase padre que pueda ser el tipo de la lista

 

cuando quieras hacer:
 

Pero pienso yo: ¿Por qe en la ultima asignacion hay conflicto?

Ah pues porque la clase padre (que tienen en comun) no es de tipo Integer. No se si meexplico... a lo que voy es que seria bueno tener un autoanalisis en tiempo de ejecucion para que se pueda determinar si es factble la asignacion o no lo es

@rojovizcaino Yeap, de

@rojovizcaino

Yeap, de acuerdo, eso es precisamente lo que terminé haciendo; creando un tipo de dato que exprese mejor mis intenciones.

@java.daba.doo

El análisis en runtime ya está, se llama "class cast exception" el que se necesita es en tiempo de compilación. Interesante tema quizá luego podamos profundizar al respecto.

+1 por acordarte de :
  se podría crear un método de extensión
  para poder escribir:

 

:)

Existe validacion, pero a

Existe validacion, pero a poco (por ejemplo en la lista) te examina los objetos que contiene "en tiempo de ejecucion" y te devuelve una coleccion de la clase padre de todos los objetos?

A eso me queria yo referir