java: (String []) List.toArray () fornece ClassCastException

107

O código a seguir (executado no Android) sempre me dá uma ClassCastException na 3ª linha:

final String[] v1 = i18nCategory.translation.get(id);
final ArrayList<String> v2 = new ArrayList<String>(Arrays.asList(v1));
String[] v3 = (String[])v2.toArray();

Acontece também quando v2 é Object [0] e também quando há Strings nele. Alguma ideia do porquê?

Gavriel
fonte
Você pode querer ler sobre Covariância e Contravariância - en.wikipedia.org/wiki/…
Hut8
E o caso em que T é uma interface com um método de fábrica para instanciação.
sebaj
2
@LaceCard - está apenas indiretamente relacionado à covariância / contravariância. O verdadeiro problema é que isso é uma consequência direta do comportamento especificado do toArray()método.
Stephen C

Respostas:

250

Isso ocorre porque quando você usa

 toArray() 

ele retorna um Object [], que não pode ser lançado em um String [] (mesmo que o conteúdo seja Strings). Isso ocorre porque o método toArray só obtém um

List 

e não

List<String>

como os genéricos são apenas uma coisa de código-fonte, e não estão disponíveis em tempo de execução, portanto, ele não pode determinar que tipo de array criar.

usar

toArray(new String[v2.size()]);

que aloca o tipo certo de array (String [] e do tamanho certo)

MeBigFatGuy
fonte
3
porque genéricos, é por isso
Kaleb Brasee
2
@Kaleb - não é verdade. Ou pelo menos esse não é o motivo original. Os toArraymétodos existiam antes das classes de coleção serem genéricas.
Stephen C de
10
Esta é a solução correta, mas não a explicação correta. Você não pode lançar Object [] para Double [] porque é um recurso de linguagem, nada mais. Não tem a ver com genéricos. Você pode lançar Object to Double assumindo que seja realmente um Double. Então, logicamente, você poderia fazer o mesmo com um array, mas ele simplesmente não faz parte da linguagem. Se você lançar Object to Double, haverá uma verificação em tempo de execução para ter certeza de que o objeto é realmente um Double. Se você lançar Double para Object, não haverá verificação de tempo de execução, uma vez que Object está na hierarquia de herança de Double.
KyleM
5
Ao fazer isso no IntelliJ, recebo um aviso para usar em toArray(new String[0])vez disso: "Em versões anteriores do Java, o uso de array pré-dimensionado era recomendado, pois a chamada de reflexão necessária para criar um array de tamanho adequado era bastante lenta. No entanto, desde as últimas atualizações do OpenJDK 6 essa chamada foi intrinsecada, tornando o desempenho da versão de array vazio o mesmo e às vezes até melhor. Também passar array pré-dimensionado é perigoso para uma coleção simultânea, pois uma corrida é possível entre a chamada sizee toArray. "
Mikepote
35

Você está usando o errado toArray()

Lembre-se de que os genéricos do Java são principalmente açúcar sintático. Um ArrayList não sabe realmente que todos os seus elementos são Strings.

Para resolver seu problema, ligue toArray(T[]). No seu caso,

String[] v3 = v2.toArray(new String[v2.size()]);

Observe que o formulário genérico toArray(T[])retorna T[], portanto, o resultado não precisa ser convertido explicitamente.

Dilum Ranatunga
fonte
1
String[] v3 = v2.toArray(new String[0]); 

também funciona, observe que você não precisa nem lançar mais uma vez que o ArrayType correto é fornecido ao método.

Esperançosamente útil
fonte
0
String[] str = new String[list.size()];
str = (String[]) list.toArray(str);

Use assim.

MakNe
fonte
1
Por favor! Formate seu código! Selecione tudo isso pressionando "ctrl + k", ou adicione "` "antes da primeira letra do código e no último uma outra. Você também pode selecionar "{}" na ajuda no topo ao escrever a resposta!
MK de