una tutorial son qué que pila metodos genérica genericos generica generic ejemplos clase arreglos java type-erasure

tutorial - Java-¿Cómo lidiar con el borrado de tipos en constructores?



tutorial generics java (5)

1: Mantener la compatibilidad hacia atrás con el borrado.

2: ¿Pueden tu clase usar genéricos? Algo como esto:

public class User<T> { private List<T> whatever; public User(List<T> source){ .... } }

No estoy seguro de si esto es lo que quisiste decir con (2)

Digamos que tengo dos constructores en mi clase:

public User (List<Source1> source){ ... } public User (List<Source2> source) { ... }

Digamos que ambos constructores proporcionan la misma información sobre un usuario y son formas igualmente válidas de construir un usuario para diferentes casos de uso.

En Java, no puede hacer esto debido al borrado de tipo: Java no aceptará dos constructores que tengan como lista de parámetros <? >.

Entonces, ¿cuál es la manera de evitar esto? ¿Qué es una solución que no es una exageración pero que respeta la OO básica? Parece incorrecto tener que construir un método de fábrica u otra interfaz alrededor de esto solo porque Java no tiene un soporte genérico fuerte.

Aquí están las posibilidades que puedo pensar:

1) Acepte una List<?> Como parámetro para el constructor y analice en el constructor el tipo de lógica que necesita, o lance una excepción si no es ninguno de los tipos aceptados.

2) Cree una clase que acepte cualquiera de las listas, construya el objeto de usuario apropiado y lo devuelva.

3) Cree envoltorios alrededor de la List<Source1> y la List<Source2> que pueden pasarse al constructor del Usuario en su lugar.

4) Subclasifique a este tipo con dos clases, donde se hereda toda la funcionalidad, excepto el constructor. El constructor de uno acepta Source1, el otro acepta Source2.

5) Envuelva a este tipo con un constructor, donde hay dos métodos de construcción diferentes para las dos fuentes diferentes de datos para la creación de instancias.

Mis preguntas son estas:

1) ¿Es la necesidad de hacer esto una falla con Java o una decisión de diseño intencional? ¿Qué es la intuición?

2) ¿Qué solución es más sólida en términos de mantener un buen código sin introducir una complejidad innecesaria? ¿Por qué?

Esta pregunta es similar: diseñar constructores alrededor del borrado de tipos en Java, pero no entra en detalles, solo sugiere varias soluciones.


El enfoque habitual es utilizar métodos de fábrica :

public static User createFromSource1(List<Source1> source) { User user = new User(); // build your User object knowing you have Source1 data return user; } public static User createFromSource2(List<Source2> source) { User user = new User(); // build your User object knowing you have Source2 data return user; }

Si solo desea construir utilizando Source1 o Source2 (es decir, no tiene un constructor predeterminado), simplemente oculte su constructor, lo que obligará a los clientes a usar sus métodos de fábrica:

private User () { // Hide the constructor }

Este problema surge porque no puede nombrar a los constructores de manera diferente, que sería la forma en que lo superaría si estos fueran métodos normales. Debido a que los nombres de los constructores son fijos como el nombre de la clase, este patrón de código es solo una manera de distinguir y luego dar el mismo tipo de borrado.


El problema básico es que el lenguaje se diseñó (con el nombre del constructor siendo fijo) antes de que existieran los genéricos, por lo que no puede manejar las colisiones debido al borrado de tipo, que normalmente se manejaría al cambiar el nombre de los métodos para distinguirlos.

Una "solución alternativa", sin recurrir a los métodos de fábrica, es agregar otro parámetro no escrito para que el compilador pueda distinguirlos:

public User(List<Source1>, Source1 instance) { // ignore instance } public User(List<Source2>, Source2 instance) { // ignore instance }

Sin embargo, esto es un poco cojo, ya que podría reemplazar esos parámetros adicionales con cualquier cosa (por ejemplo, Integer y String , o simplemente hacer que uno de ellos omita el segundo parámetro) y aún funcionaría. Además, el parámetro extra se ignora; existe solo para distinguir a los constructores. Sin embargo, permite que el código funcione sin agregar ningún método adicional o código especial.


Me gusta la idea de Fábrica en general, o Usuario genérico (como lo sugiere @Markus Mikkolainen), pero una posible alternativa sería pasar la clase de la lista como un segundo argumento y activarlo, por ejemplo

public User<List<?> source, Class<?> clazz) { switch(clazz) { case Source1.class: initForSource1(); break; case Source2.class: initForSource2(); break; default: throw new IllegalArgumentException(); } }

El <?> Podría ser otra cosa si hay alguna clase ancestral común. Puedo imaginar muchos casos en los que esta es una mala idea, pero algunos en los que podría ser aceptable.


public class User<T> { public User(List<T> source){ } }

o probablemente mejor:

public class User<T extends SomeTypeOfYours> { public User(List<T> source){ } }

Wheer SomeTypeOfYours es un Source1 de Source1 y Source2 .