Esto no se compila, se agradece cualquier sugerencia.
...
List<Object> list = getList();
return (List<Customer>) list;
El compilador dice: no se puede convertir List<Object>
aList<Customer>
Esto no se compila, se agradece cualquier sugerencia.
...
List<Object> list = getList();
return (List<Customer>) list;
El compilador dice: no se puede convertir List<Object>
aList<Customer>
Respuestas:
siempre puede convertir cualquier objeto a cualquier tipo al convertirlo primero en Object. en tu caso:
(List<Customer>)(Object)list;
debe asegurarse de que en tiempo de ejecución la lista no contenga más que objetos Customer.
Los críticos dicen que tal conversión indica algo mal en su código; debería poder modificar sus declaraciones de tipo para evitarlo. Pero los genéricos de Java son demasiado complicados y no son perfectos. A veces simplemente no sabes si hay una solución bonita para satisfacer al compilador, aunque conoces muy bien los tipos de tiempo de ejecución y sabes que lo que estás intentando hacer es seguro. En ese caso, simplemente haga el vaciado crudo según sea necesario, para que pueda dejar el trabajo para ir a casa.
@SuppressWarnings("unchecked")
. Tenga en cuenta que también puede realizar upcast a en (List)
lugar de a (Object)
.
Esto se debe a que, aunque un cliente es un objeto, una lista de clientes no es una lista de objetos. Si es así, puede poner cualquier objeto en una lista de Clientes.
.Cast<T>()
y otro llamado .OfType<T>()
. El primero realiza un lanzamiento en cada elemento (lanzando las excepciones deseadas) mientras que el segundo filtra los elementos que no se pueden lanzar (por lo que elegiría uno según su escenario de uso).
instanceof
cliente?
Dependiendo de su otro código, la mejor respuesta puede variar. Tratar:
List<? extends Object> list = getList();
return (List<Customer>) list;
o
List list = getList();
return (List<Customer>) list;
Pero tenga en cuenta que no se recomienda hacer lanzamientos sin control.
Con Java 8 Streams :
A veces, el casting de fuerza bruta está bien:
List<MyClass> mythings = (List<MyClass>) (Object) objects
Pero aquí hay una solución más versátil:
List<Object> objects = Arrays.asList("String1", "String2");
List<String> strings = objects.stream()
.map(element->(String) element)
.collect(Collectors.toList());
Hay un montón de beneficios, pero uno es que puede emitir su lista de manera más elegante si no puede estar seguro de lo que contiene:
objects.stream()
.filter(element->element instanceof String)
.map(element->(String)element)
.collect(Collectors.toList());
FluentIterable
funcionó para mí.
Puedes usar un yeso doble.
return (List<Customer>) (List) getList();
Tenga en cuenta que no soy un programador de Java, pero en .NET y C #, esta característica se llama contravarianza o covarianza. No he profundizado en esas cosas todavía, ya que son nuevas en .NET 4.0, que no estoy usando ya que es solo beta, así que no sé cuál de los dos términos describe su problema, pero permítame describir el problema técnico con esto.
Supongamos que se le permitió emitir. Tenga en cuenta que digo emitir , ya que eso es lo que dijo, pero hay dos operaciones que podrían ser posibles, emitir y convertir .
Convertir significaría que obtiene un nuevo objeto de lista, pero dice conversión, lo que significa que desea tratar temporalmente un objeto como otro tipo.
Aquí está el problema con eso.
¿Qué pasaría si se permitiera lo siguiente?
List<Object> list = getList();
List<Customer> customers = (List<Customer>)list;
list.Insert(0, new someOtherObjectNotACustomer());
Customer c = customers[0];
En este caso, esto intentaría tratar un objeto, que no es un cliente, como un cliente, y obtendría un error de tiempo de ejecución en un punto, ya sea dentro de la lista o desde la asignación.
Sin embargo, se supone que los genéricos le brindan tipos de datos con seguridad de tipos, como colecciones, y dado que les gusta usar la palabra 'garantizado', este tipo de transmisión, con los problemas que siguen, no está permitido.
En .NET 4.0 (lo sé, tu pregunta era sobre java), esto estará permitido en algunos casos muy específicos , donde el compilador puede garantizar que las operaciones que haces son seguras, pero en el sentido general, este tipo de conversión no lo hará. ser permitido. Lo mismo se aplica a Java, aunque no estoy seguro de ningún plan para introducir covarianza y contravarianza en el lenguaje Java.
Con suerte, alguien con mejores conocimientos de Java que yo pueda decirle los detalles para el futuro o la implementación de Java.
Otro enfoque sería utilizar una secuencia de Java 8.
List<Customer> customer = myObjects.stream()
.filter(Customer.class::isInstance)
.map(Customer.class::cast)
.collect(toList());
List<Customer> cusList = new ArrayList<Customer>();
for(Object o: list){
cusList.add((Customer)o);
}
return cusList;
list.stream().forEach(x->cusList.add((Customer)x))
return cuslist;
No puede porque List<Object>
y List<Customer>
no están en el mismo árbol de herencia.
Puede agregar un nuevo constructor a su List<Customer>
clase que tome a List<Object>
y luego iterar a través de la lista, convirtiendo cada uno en Object
a Customer
y agregándolo a su colección. Tenga en cuenta que puede producirse una excepción de conversión no válida si la persona que llama List<Object>
contiene algo que no es Customer
.
El objetivo de las listas genéricas es restringirlas a ciertos tipos. Está intentando tomar una lista que puede contener cualquier cosa (pedidos, productos, etc.) y meterla en una lista que solo puede incluir clientes.
Su mejor opción es crear una nueva List<Customer>
, iterar a través de List<Object>
, agregar cada elemento a la nueva lista y devolverlo.
Como han señalado otros, no puedes lanzarlos de forma segura, ya que a List<Object>
no es un List<Customer>
. Lo que podría hacer es definir una vista en la lista que realice una verificación de tipo in situ. Usando colecciones de Google que serían:
return Lists.transform(list, new Function<Object, Customer>() {
public Customer apply(Object from) {
if (from instanceof Customer) {
return (Customer)from;
}
return null; // or throw an exception, or do something else that makes sense.
}
});
Similar con Bozho arriba. Puede hacer una solución aquí (aunque a mí no me gusta) a través de este método:
public <T> List<T> convert(List list, T t){
return list;
}
Si. Convertirá su lista en su tipo genérico solicitado.
En el caso anterior, puede hacer un código como este:
List<Object> list = getList();
return convert(list, new Customer());
Dependiendo de lo que quiera hacer con la lista, es posible que ni siquiera necesite enviarla a un List<Customer>
. Si solo desea agregar Customer
objetos a la lista, puede declararlo de la siguiente manera:
...
List<Object> list = getList();
return (List<? super Customer>) list;
Esto es legal (bueno, no solo legal, sino correcto , la lista es de "algún supertipo para el cliente"), y si lo va a pasar a un método que simplemente agregará objetos a la lista, entonces lo anterior los límites genéricos son suficientes para esto.
Por otro lado, si desea recuperar objetos de la lista y escribirlos fuertemente como Clientes, no tiene suerte, y con razón. Porque la lista es unaList<Object>
no hay garantía de que los contenidos sean clientes, por lo que tendrá que proporcionar su propio casting en la recuperación. (O estar realmente, absolutamente, doblemente seguro de que la lista solo contendrá Customers
y usará una conversión doble de una de las otras respuestas, pero tenga en cuenta que está eludiendo por completo la seguridad de tipos en tiempo de compilación que obtiene de los genéricos en este caso).
En términos generales, siempre es bueno considerar los límites genéricos más amplios posibles que serían aceptables al escribir un método, doblemente si se va a utilizar como método de biblioteca. Si solo va a leer de una lista, use en List<? extends T>
lugar de List<T>
, por ejemplo, esto le da a sus llamantes mucho más alcance en los argumentos que pueden transmitir y significa que es menos probable que se encuentren con problemas evitables similares al que usted ' estás teniendo aquí.