Como transmitir List <Object> para List <MyClass>


Respostas:


156

você sempre pode converter qualquer objeto para qualquer tipo, fazendo o up-casting para o objeto primeiro. no seu caso:

(List<Customer>)(Object)list; 

você deve ter certeza de que, em tempo de execução, a lista não contém nada além de objetos Cliente.

Os críticos dizem que tal elenco indica algo errado com seu código; você deve ser capaz de ajustar suas declarações de tipo para evitá-lo. Mas os genéricos Java são muito complicados e não são perfeitos. Às vezes, você simplesmente não sabe se existe uma solução bonita para satisfazer o compilador, embora conheça muito bem os tipos de tempo de execução e saiba que o que está tentando fazer é seguro. Nesse caso, basta fazer a fundição bruta conforme necessário, para que você possa sair do trabalho e voltar para casa.


8
Isso também precisa @SuppressWarnings("unchecked"). Observe que você também pode fazer upcast para em (List)vez de para (Object).
200_success de

36

Isso porque, embora um cliente seja um objeto, uma lista de clientes não é uma lista de objetos. Se fosse, você poderia colocar qualquer objeto em uma lista de Clientes.


2
Não, não é. Você contornaria a segurança de tipo se o acima fosse permitido. Observe que lançar não significa criar uma nova lista e copiar os itens. Significa lidar com a única instância como um tipo diferente e, portanto, você teria uma lista que contém objetos potencialmente não-Cliente com uma garantia de segurança de tipo que não deveria. Isso não tem nada a ver com java como tal. Como você acha que esse recurso deixa o Java preso na idade das trevas, eu o desafio a explicar como você acha que isso deveria funcionar.
Lasse V. Karlsen

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

@ LasseV.Karlsen Não estou falando de casting, estou falando de conversão. Isso não contornaria a segurança de tipo, mas a aplicaria. A implementação de genéricos em Java, a falta de sobrecargas de operadores, métodos de extensão e muitas outras comodidades modernas me deixaram epicamente desapontado. - Enquanto isso, o .NET tem dois métodos de extensão separados para isso - um chamado .Cast<T>()e outro chamado .OfType<T>(). O primeiro executa uma conversão em cada elemento (lançando as exceções desejadas), enquanto o último filtra os elementos que não podem ser lançados (então você escolheria um dependendo do seu cenário de uso).
BrainSlugs83

1
@EAGER_STUDENT Eu não colocaria além do Java que isso pode realmente funcionar (todo esse negócio de eliminação de tipo ridículo, eu teria que tentar ...) - mas não, eu nunca escreveria um código assim - você perde segurança de tipo, o que acontece se um elemento da coleção não for um instanceofcliente?
BrainSlugs83

1
@ BrainSlugs83 A pessoa que fez a pergunta perguntou especificamente sobre o elenco. E se Java ainda não tiver os métodos relevantes, como os métodos .NET aos quais você se refere (que ainda não estão convertendo, aliás), então você deve ser capaz de adicioná-los facilmente. Isso tudo é meio ortogonal à questão em questão, que perguntava sobre sintaxe específica.
Lasse V. Karlsen

35

Dependendo do seu outro código, a melhor resposta pode variar. Experimentar:

List<? extends Object> list = getList();
return (List<Customer>) list;

ou

List list = getList();
return (List<Customer>) list;

Mas tenha em mente que não é recomendável fazer essas conversões não verificadas.


3
-1 - este é um péssimo hábito de se adquirir, e a menos que você use a anotação "Unchecked", o compilador ainda reclamará sobre isso
kdgregory

24

Com Java 8 Streams :

Às vezes, o lançamento de força bruta é bom:

List<MyClass> mythings = (List<MyClass>) (Object) objects

Mas aqui está uma solução mais versátil:

List<Object> objects = Arrays.asList("String1", "String2");

List<String> strings = objects.stream()
                       .map(element->(String) element)
                       .collect(Collectors.toList());

Há muitos benefícios, mas um é que você pode lançar sua lista de maneira mais elegante se não tiver certeza do que ela contém:

objects.stream()
    .filter(element->element instanceof String)
    .map(element->(String)element)
    .collect(Collectors.toList());

1
Isso é mais uma cópia do que um elenco, no entanto.
200_success de

O elenco mencionado no anser aceito não funcionou para mim. Também estava no Java 7. Mas o Guava FluentIterablefuncionou para mim.
Sridhar Sarnobat

Isso é o que eu estava procurando, só não conhecia a sintaxe
Fueled By Coffee

23

Você pode usar um elenco duplo.

return (List<Customer>) (List) getList();

8

Observe que não sou um programador java, mas em .NET e C #, esse recurso é chamado de contravariância ou covariância. Ainda não me aprofundei nessas coisas, já que são novas no .NET 4.0, que não estou usando porque é apenas beta, então não sei qual dos dois termos descreve o seu problema, mas deixe-me descrever o problema técnico com isso.

Vamos supor que você tenha permissão para lançar. Observe, eu digo elenco , já que foi o que você disse, mas há duas operações que poderiam ser possíveis, casting e conversão .

A conversão significaria que você obteria um novo objeto de lista, mas você diria fundição, o que significa que deseja tratar temporariamente um objeto como outro tipo.

Aqui está o problema com isso.

O que aconteceria se o seguinte fosse permitido (observe, estou assumindo que antes do elenco, a lista de objetos na verdade contém apenas objetos do Cliente, caso contrário, o elenco não funcionaria mesmo nesta versão hipotética de java):

List<Object> list = getList();
List<Customer> customers = (List<Customer>)list;
list.Insert(0, new someOtherObjectNotACustomer());
Customer c = customers[0];

Nesse caso, isso tentaria tratar um objeto, que não é um cliente, como um cliente, e você obteria um erro de tempo de execução em um ponto, seja do formulário dentro da lista ou da atribuição.

Os genéricos, no entanto, devem fornecer tipos de dados seguros para tipos, como coleções, e como gostam de usar a palavra "garantido", esse tipo de conversão, com os problemas que se seguem, não é permitido.

No .NET 4.0 (eu sei, sua pergunta era sobre java), isso será permitido em alguns casos bem específicos , onde o compilador pode garantir que as operações que você faz são seguras, mas no sentido geral, este tipo de elenco não vai ser permitido. O mesmo vale para java, embora eu não tenha certeza sobre quaisquer planos para introduzir co- e contravariância para a linguagem java.

Esperançosamente, alguém com melhor conhecimento em java do que eu pode lhe dizer os detalhes para o futuro ou implementação de java.


3
O futuro do Java é ... Scala. Sério, o cara que colocou os genéricos no Java desenvolveu uma nova linguagem que é vagamente semelhante ao Java, mas muito, muito proficiente com tipos: uma implementação muito completa e consistente de manipulação de tipos. Acho que ninguém sabe ao certo quais recursos do Scala retornarão ao Java e quando.
Carl Smotricz

uma excelente explicação da covariância que realmente responde à pergunta dos OPs. Bem feito.
Kevin Day

Carl: Achei que alguns desenvolvedores Java foram adiante para criar C #. :) De qualquer forma, o Java provavelmente está indo na direção do Scala no futuro ao invés de, digamos, algo menos fortemente tipado.
Esko

@Carl - em Scala, há uma diferença sutil em que, por padrão, as listas são imutáveis. Portanto, geralmente você não tem o problema de adicionar um objeto a uma lista de clientes, pois, ao fazer isso, obtém uma nova lista de objetos .
Brian Agnew

Er ... tecnicamente isso está correto - mas mesmo antes do .NET 4.0 - você poderia fazer isso com métodos de extensão IEnumerable regulares (.Cast <> e .OfType <>) - então não há necessidade de ir longe se você deseja apenas uma iteração de tipo forte.
BrainSlugs83

7

Outra abordagem seria usar um fluxo java 8.

    List<Customer> customer = myObjects.stream()
                                  .filter(Customer.class::isInstance)
                                  .map(Customer.class::cast)
                                  .collect(toList());

1
obrigado, esta solução é tão bonita, usando a referência de método
thang

1
Nunca pensei que alguém
fosse

3

Você deve apenas iterar sobre a lista e lançar todos os objetos um por um


3

Você pode fazer algo assim

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

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

return cusList; 

Ou a maneira Java 8

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

return cuslist;

2

Você não pode porque List<Object>e List<Customer>não estão na mesma árvore de herança.

Você poderia adicionar um novo construtor à sua List<Customer>classe que leva a List<Object>e, em seguida, iterar pela lista, convertendo cada Objectum em a Customere adicionando-o à sua coleção. Esteja ciente de que uma exceção de conversão inválida pode ocorrer se o chamador List<Object>contiver algo que não seja um Customer.

O objetivo das listas genéricas é restringi-las a certos tipos. Você está tentando pegar uma lista que pode conter qualquer coisa (Pedidos, Produtos, etc.) e comprimi-la em uma lista que pode conter apenas Clientes.


2

Você pode criar uma nova lista e adicionar os elementos a ela:

Por exemplo:

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

1

Sua melhor aposta é criar um novo List<Customer>, iterar através do List<Object>, adicionar cada item à nova lista e devolvê-lo.


1

Como outros apontaram, você não pode conjurá-los de maneira segura, já que a List<Object>não é a List<Customer>. O que você pode fazer é definir uma visualização na lista que faça a verificação de tipo no local. Usando coleções do Google que seriam:

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

Semelhante ao Bozho acima. Você pode fazer alguma solução alternativa aqui (embora eu mesmo não goste) por meio deste método:

public <T> List<T> convert(List list, T t){
    return list;
}

Sim. Ele vai lançar sua lista em seu tipo genérico exigido.

No caso fornecido acima, você pode fazer algum código como este:

    List<Object> list = getList();
    return convert(list, new Customer());

Eu gosto dessa solução. Mesmo se você precisar adicionar SuppressWarnings, é melhor adicioná-lo em um só lugar do que em todos os lançamentos inseguros.
robson

1

Dependendo do que você deseja fazer com a lista, pode nem ser necessário convertê-la em a List<Customer>. Se você deseja apenas adicionar Customerobjetos à lista, pode declará-lo da seguinte maneira:

...
List<Object> list = getList();
return (List<? super Customer>) list;

Isso é legal (bem, não apenas legal, mas correto - a lista é de "algum supertipo para o cliente"), e se você for transferi-la para um método que apenas adicionará objetos à lista, então o acima limites genéricos são suficientes para isso.

Por outro lado, se você deseja recuperar objetos da lista e tê-los fortemente tipados como Clientes - então você está sem sorte, e com razão. Como a lista é um, List<Object>não há garantia de que os conteúdos sejam clientes, então você terá que fornecer seu próprio elenco na recuperação. (Ou esteja realmente, absolutamente, duplamente certo de que a lista só conterá Customerse usará um duplo elenco de uma das outras respostas, mas perceba que você está contornando completamente a segurança de tipo em tempo de compilação que obtém dos genéricos neste caso).

Em termos gerais, é sempre bom considerar os limites genéricos mais amplos possíveis que seriam aceitáveis ​​ao escrever um método, duplamente se for usado como um método de biblioteca. Se você for ler apenas de uma lista, use em List<? extends T>vez de List<T>, por exemplo - isso dá aos chamadores muito mais escopo nos argumentos que podem passar e significa que eles são menos propensos a encontrar problemas evitáveis ​​semelhantes ao que você ' está tendo aqui.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.