Cómo convertir List <Object> a List <MyClass>


Respuestas:


156

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.


8
Esto también necesita @SuppressWarnings("unchecked"). Tenga en cuenta que también puede realizar upcast a en (List)lugar de a (Object).
200_success

36

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.


2
No, no lo es. Eludiría la seguridad de tipos si se permitiera lo anterior. Tenga en cuenta que la transmisión no significa crear una nueva lista y copiar los elementos. Significa manejar la instancia única como un tipo diferente y, por lo tanto, tendría una lista que contiene objetos potencialmente que no son de Cliente con una garantía de seguridad de tipos que no debería. Esto no tiene nada que ver con Java como tal. Dado que cree que esta característica deja a Java atascado en la edad oscura, lo desafío a que explique cómo cree que debería funcionar.
Lasse V. Karlsen

1
@ BrainSlugs83 para su necesidad (List <Customer>) (Object) list;
Muthu Ganapathy Nathan

@ LasseV.Karlsen No estoy hablando de casting, estoy hablando de conversión. No eludiría la seguridad de tipos, la haría cumplir. La implementación de genéricos de Java, la falta de sobrecargas de operadores, los métodos de extensión y muchas otras comodidades modernas me han decepcionado de manera épica. - Mientras tanto, .NET tiene dos métodos de extensión separados para esto: uno llamado .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).
BrainSlugs83

1
@EAGER_STUDENT No pondría más allá de Java que eso realmente podría funcionar (todo este ridículo negocio de borrado de tipos, tendría que intentarlo ...) - pero no, nunca escribiría un código como ese - pierdes seguridad de tipos, ¿qué sucede si un elemento de la colección no es un instanceofcliente?
BrainSlugs83

1
@ BrainSlugs83 La persona que hizo la pregunta preguntó específicamente sobre el casting. Y si Java aún no tiene los métodos relevantes como los métodos .NET a los que hace referencia (que aún no se están convirtiendo por cierto), entonces debería poder agregarlos fácilmente. Sin embargo, todo esto es un poco ortogonal a la pregunta en cuestión, que preguntaba sobre una sintaxis específica.
Lasse V. Karlsen

35

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.


3
-1 - este es un hábito realmente malo, y a menos que use la anotación "Sin marcar", el compilador seguirá quejándose al respecto
kdgregory

24

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());

1
Sin embargo, eso es más una copia que un elenco.
200_success

El casting mencionado en el anser aceptado no funcionó para mí. También estaba en Java 7. Pero Guava FluentIterablefuncionó para mí.
Sridhar Sarnobat

Esto es lo que estaba buscando, simplemente no conocía la sintaxis
Fueled By Coffee


8

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.


3
El futuro de Java es ... Scala. En serio, el tipo que incorporó genéricos a Java ha desarrollado un nuevo lenguaje que es vagamente similar a Java pero realmente muy competente con los tipos: una implementación muy completa y consistente del manejo de tipos. Creo que nadie sabe con certeza qué características de Scala regresarán a Java y cuándo.
Carl Smotricz

una excelente explicación de la covarianza que realmente responde a la pregunta de los OP. Bien hecho.
Kevin Day

Carl: ¿Pensé que algunos desarrolladores de Java continuaron con la creación de C #? :) De todos modos, sí, lo más probable es que Java vaya en la dirección de Scala en el futuro en lugar de, digamos, algo menos tipado.
Esko

@Carl: en Scala hay una sutil diferencia en que, por defecto, las Listas son inmutables. Por lo general, no tiene el problema de agregar un Objeto a una lista de Clientes, ya que cuando hace esto, obtiene una nueva lista de Objetos .
Brian Agnew

Er ... técnicamente esto es correcto, pero incluso antes de .NET 4.0, podría hacer esto con los métodos de extensión IEnumerable regulares (.Cast <> y .OfType <>), por lo que no es necesario ir al extremo profundo si solo desea una iteración de tipo fuerte.
BrainSlugs83

7

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());

1
gracias, esta solución es tan hermosa, usando la referencia del método
thang

1
Nunca pensé que alguien se desplazaría hacia abajo tan lejos: D
d0x

3

Debería iterar sobre la lista y lanzar todos los Objetos uno por uno


3

Puedes hacer algo como esto

List<Customer> cusList = new ArrayList<Customer>();

for(Object o: list){        
    cusList.add((Customer)o);        
}

return cusList; 

O al modo Java 8

list.stream().forEach(x->cusList.add((Customer)x))

return cuslist;

2

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 Objecta Customery 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.


2

Puede crear una nueva lista y agregarle los elementos:

Por ejemplo:

List<A> a = getListOfA();
List<Object> newList = new ArrayList<>();
newList.addAll(a);

1

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.


1

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.
  }
});

1

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());

Me gusta esta solucion Incluso si necesita agregar SuppressWarnings, es mejor agregarlo en un solo lugar que en cada lanzamiento inseguro.
robson

1

Dependiendo de lo que quiera hacer con la lista, es posible que ni siquiera necesite enviarla a un List<Customer> . Si solo desea agregar Customerobjetos 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á Customersy 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í.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.