Eu tenho um List<SubClass>
que eu quero tratar como um List<BaseClass>
. Parece que não deve ser um problema, já que converter um SubClass
para um BaseClass
é muito fácil, mas meu compilador reclama que o elenco é impossível.
Então, qual é a melhor maneira de obter uma referência aos mesmos objetos que um List<BaseClass>
?
No momento, estou apenas fazendo uma nova lista e copiando a lista antiga:
List<BaseClass> convertedList = new ArrayList<BaseClass>(listOfSubClass)
Mas, pelo que entendi, isso precisa criar uma lista totalmente nova. Gostaria de uma referência à lista original, se possível!
java
inheritance
casting
Riley Lark
fonte
fonte
Respostas:
A sintaxe para esse tipo de atribuição usa um curinga:
É importante perceber que a não
List<SubClass>
é intercambiável com a . O código que mantém uma referência à espera que cada item da lista seja a . Se outra parte do código se referir à lista como a , o compilador não reclamará quando um ou for inserido. Mas isso causará um para o primeiro trecho de código, que assume que tudo na lista é a .List<BaseClass>
List<SubClass>
SubClass
List<BaseClass>
BaseClass
AnotherSubClass
ClassCastException
SubClass
Coleções genéricas não se comportam da mesma forma que matrizes em Java. Matrizes são covariantes; isto é, é permitido fazer o seguinte:
Isso é permitido, porque a matriz "conhece" o tipo de seus elementos. Se alguém tentar armazenar algo que não é uma instância da
SubClass
matriz (viabases
referência), uma exceção de tempo de execução será lançada.Coleções genéricas não "conhecem" seu tipo de componente; essas informações são "apagadas" no momento da compilação. Portanto, eles não podem gerar uma exceção de tempo de execução quando ocorre um armazenamento inválido. Em vez disso, a
ClassCastException
será gerado em algum ponto distante e difícil de associar no código quando um valor for lido da coleção. Se você prestar atenção aos avisos do compilador sobre segurança de tipo, evitará esses erros de tipo em tempo de execução.fonte
O erickson já explicou por que você não pode fazer isso, mas aqui estão algumas soluções:
Se você deseja apenas remover elementos de sua lista base, em princípio, seu método de recebimento deve ser declarado como a
List<? extends BaseClass>
.Mas se não estiver e você não puder alterá-lo, poderá agrupar a lista com
Collections.unmodifiableList(...)
, o que permite retornar uma Lista de um supertipo do parâmetro do argumento. (Evita o problema de segurança tipográfica lançando UnsupportedOperationException nas tentativas de inserção.)fonte
Como o @erickson explicou, se você realmente deseja uma referência à lista original, certifique-se de que nenhum código insira nada nessa lista, se você quiser usá-lo novamente sob sua declaração original. A maneira mais simples de obtê-lo é lançá-lo em uma lista simples e antiga de genéricos:
Eu não recomendaria isso se você não souber o que acontece com a Lista e sugeriria que você altere qualquer código que precise da Lista para aceitar a Lista que você possui.
fonte
Abaixo está um trecho útil que funciona. Ele constrói uma nova lista de matrizes, mas a sobrecarga de criação de objeto da JVM é significativa.
Vi outras respostas não necessariamente necessárias.
fonte
Perdi a resposta em que você acabou de lançar a lista original, usando um elenco duplo. Então, aqui está a completude:
Nada é copiado e a operação é rápida. No entanto, você está enganando o compilador aqui, portanto, certifique-se de não modificar a lista de maneira que
subList
comece a conter itens de um subtipo diferente. Ao lidar com listas imutáveis, isso geralmente não é um problema.fonte
List<BaseClass> convertedList = Collections.checkedList(listOfSubClass, BaseClass.class)
fonte
O que você está tentando fazer é muito útil e acho que preciso fazê-lo com muita frequência no código que escrevo. Um exemplo de caso de uso:
Digamos que temos uma interface
Foo
e que temos umzorking
pacoteZorkingFooManager
que cria e gerencia instâncias do pacote-privadoZorkingFoo implements Foo
. (Um cenário muito comum.)Portanto,
ZorkingFooManager
precisa conter um,private Collection<ZorkingFoo> zorkingFoos
mas precisa expor apublic Collection<Foo> getAllFoos()
.A maioria dos programadores de Java não pensaria duas vezes antes de implementar
getAllFoos()
como alocar um novoArrayList<Foo>
, preenchê-lo com todos os elementoszorkingFoos
e retorná-lo. Gosto de pensar que cerca de 30% de todos os ciclos de relógio consumidos pelo código java executado em milhões de máquinas em todo o planeta não fazem nada além de criar cópias inúteis de ArrayLists que são microssegundos após a criação.A solução para esse problema é, obviamente, fazer o downcast da coleção. Aqui está a melhor maneira de fazer isso:
O que nos leva à
castList()
função:A
result
variável intermediária é necessária devido a uma perversão da linguagem java:return (List<T>)list;
produz uma exceção "elenco não verificado"; Por enquanto, tudo bem; mas então:@SuppressWarnings( "unchecked" ) return (List<T>)list;
é um uso ilegal da anotação de suprimir avisos.Portanto, mesmo que não seja kosher usar
@SuppressWarnings
em umareturn
declaração, aparentemente é bom usá-la em uma atribuição, portanto a variável extra "resultado" resolve esse problema. (Deve ser otimizado para fora pelo compilador ou pelo JIT de qualquer maneira.)fonte
Algo assim também deve funcionar:
Realmente não sei por que o clazz é necessário no Eclipse.
fonte
Que tal lançar todos os elementos. Ele criará uma nova lista, mas fará referência aos objetos originais da lista antiga.
fonte
Este é o trecho de código completo usando Genéricos, para converter a lista de subclasses em superclasses.
Método de chamada que passa o tipo de subclasse
Método Callee que aceita qualquer subtipo da classe base
fonte