toArray dúvida

25 respostas
R

Pessoal, qual o problema com o trecho de código abaixo:

class Teste{
   public static void main (String... args){
       List <Integer> list = new ArrayList <Integer>();
       for ( int i = 0; i < 4; i++)
              list.add(i);

       Integer[] array = (Integer[]) list.toArray(); // essa linha dá classCastException
   }
}
Até onde entendo um Object[] pode ser convertido pra um Integer[]..ou não?

25 Respostas

R

É possível converter uma super para uma subclasse ?

Não seria ao contrário?

Pq a subclasse tem membros que a super não tem, por serem da sub.

S

assim funciona

public static void main(String... args)
    {
        List&lt;Integer&gt; list = new ArrayList&lt;Integer&gt;();
        for (Integer i = 0; i &lt; 4; i++) {
            list.add(i);
        }

        Integer[] array = new Integer[4];
        list.toArray(array);
    }
V
Integer[] array = (Integer[]) list.toArray(new Integer[list.size()]);
P

Não precisei usar cast , aqui funcionou assim:

public static void main (String[] args){  
	       ArrayList <Integer> list = new ArrayList <Integer>();  
	       for ( int i = 0; i < 4; i++) { 
	              list.add(i);  
	       }
	       Integer[] array = list.toArray(new Integer[list.size()]);
	       
	       for(Integer i : array){
	    	   System.out.println(i);
	       }
	   }
V

toArray() - sem parâmetros - retorna um Object[]. Ao dar cast para Integer[], você vai ter um ClassCastException.
toArray(Object[]) popula o array dado como parâmetro, ou, se não houver espaço neste, cria reflexivamente um novo array do mesmo tipo do que você passou como parâmetro.

V

Entao cara, o negocio é que o Java nao realiza o cast dos elementos do array Object[]. Imagine a seguinte situacao:

e agora

finalmente

Grandes problemas!!!

Abracao.

P

Teste este código:

Object a = new Object();
    Integer b = (Integer) a;

Dá o mesmo exception.

R
Entao cara, o negocio é que o Java nao realiza o cast dos elementos do array Object[]. Imagine a seguinte situacao: Integer[] x = {1, 2, 3}; e agora Object[] y = {1, "vinicius", new File()};

Essa me pareceu a explicação mais plausível.

Teste este código:
1. Object a = new Object();  
   2. Integer b = (Integer) a;

Object a = new Object(); Integer b = (Integer) a;

Dá o mesmo exception.

Não não, aqui não deu erro, e acho que nem deveria, isso nada mais é que um downcast.

É possível converter uma super para uma subclasse ?

Não seria ao contrário?

Pq a subclasse tem membros que a super não tem, por serem da sub.

É justamente pra isso que serve o downcast. Você avisa pro compilador que está disposto a perder a precisão de um "super-objeto" pra poder usar
as funcionalides que só um "sub-objeto" pode ter. Caso não seja possível o downcast, aí vem a ClassCastException.

Converter uma subclasse pra uma superclasse não faz sentido, o compilador já faz isso sozinho.

V

Entao phpinheiro , o problema agora é outro. Aqui voce esta violando os fundamentos de herancao, em outros termos, um Integer é um Object, porem um Object nao é um Integer. E foi que voce disse ao compilador! Resumindo, o que voce codificou é análogo a seguinte instrução:

Integer inteiro = new Object();

E isto significa: um Object é um inteiro!

Abracao

R

E se eu tiver um método com um Object como parâmetro e quiser realizar operações da classe Integer nesse objeto.
Primeiro preciso converter o Object pra Integer, certo?

Ah sim, agora entendi.

Isso aqui funciona:

Object o = new Integer(2);
		Integer i = (Integer) o;
R

Isso aqui tbm funciona:

Object []o = new Integer[2]; Integer []i = (Integer[]) o;

Agora a seguinte linha:

Integer[] array = (Integer[]) list.toArray();

o toArray() retorna um Object[]. Não é uma referência do tipo Object[] que aponta pra um Integer[]. De fato o objeto é um Object[]. Por isso não funciona. Certo?

V

Boa, garoto!! Isso mesmo!!

P

Vixi…me confundi…

Não é a mesma coisa do meu exemplo? tirando que o meu exemplo é um Object, e o outro é um Object[].

V

http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collection.html

V

Rsr…quer ver voce ficar confuso agora:

Depende como voce esta abordando o problema! Voce poderia ter inicialmente:

Que é equivalente a primeira instrucao que escrevi!

P

Exatamente Vini, dá na mesma. Isso que eu quis dizer.

estou falando porque vc mencionou anteriormente que o meu exemplo era outro problema

Era outra abordagem, realmente…mas o problema é o mesmo.

B
Tenho um problema parecido mas pouco mais complicado:
import java.util.ArrayList;

public class Cortar
{
   public static void main(String[] args)
   {
      String[] stringArray = new String[5];
      stringArray[0] = "a";
      stringArray[2] = "c";
      stringArray[4] = "e";

      Object[] objectArray = cortarNulos(stringArray);

      System.out.println("Objects: ");
      for (Object object : objectArray)
      {
         System.out.println(object.getClass());
      }

      String[] stringArray2 = cortarNulos(stringArray);

      System.out.println("Strings: ");
      for (String s : stringArray2)
      {
         System.out.println(s);
      }
   }

   public static <T> T[] cortarNulos(T[] array)
   {
      ArrayList<T> list = new ArrayList<T>(array.length);
      for (T a : array)
      {
         if (a != null)
            list.add(a);
      }

      return (T[]) list.toArray();
   }
}
Resultado:[code=xml] Objects: class java.lang.String class java.lang.String class java.lang.String Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String; at Cortar.main(Cortar.java:20) [/code] Se eu mudar o toArray para
return (T[]) list.toArray(array);
A parte de Strings imprime
Strings: 
a
c
e
null
e
Alguém sabe fazer a parte das Strings funcionar sem que eu tenha que mudar para esta linhas?
String[] stringArray2 = cortarNulos(stringArray, new String[0]);

public static <T> T[] cortarNulos(T[] array, T[] array2)

return (T[]) list.toArray(array2);

E imprimir somente:
[code=xml]Strings:
a
c
e
[/code]

V

Evite o método toArray(), e sempre prefira toArray(Object[]).
O método toArray sempre-sempre-sempre retorna um Object[]. Isso não é um Integer[] nem um T[] e nem um Cachorro[].

O método toArray() na verdade foi mal-projetado (se bem que não poderia ter sido criado de forma diferente). Ele tem uma falha de segurança de tipos inerente do type-erasure - A JVM não sabe qual é o generic do List, logo não tem como saber qual é o tipo do array. Arrays são fortemente tipados, e não é bom trazer o problema do type-erasure para eles. Portanto, evite este método.
O toArray(Object[]) é exatamente a gambiarra que foi criada especificamente para contornar este problema da linguagem.

B
victorwss:
Evite o método toArray(), e sempre prefira toArray(Object[]).

Sm, mas não resolve meu problema:

return (T[]) list.toArray(new Object[0]);

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
V
Bruno Laturner:
victorwss:
Evite o método toArray(), e sempre prefira toArray(Object[]).

Sm, mas não resolve meu problema:

return (T[]) list.toArray(new Object[0]);

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;

Isso parece um daqueles ClassCastExceptions obscuros que na verdade vem de métodos bridge, que são aqueles métodos sintéticos inseridos pelo compilador que fazem casts para lidar com generics.

O compilador deve estar criando um método bridge assim:
private static String[] cortarNulos(String[] array) {
    // Aqui o compilador linka o método original.
    return (String[]) cortarNulos(array);
}
E é este cast sintético que está gerando o seu ClassCastException.

O problema é a assinatura do método cortarNulos(T[]). Ele deve retornar Object[] e não T[].

V

Conforme em alguns post anteriores, o problema é que o método toArray(new Object[]) retorna um Object[], porem, seu metodo generico foi tipado para String e espera um retorno do tipo String[] e voce esta passando como retorno Object[].

Abracao

B

Achei uma solução

return list.toArray((T[]) Array.newInstance(list.iterator().next().getClass(), list.size()));

Obs: tem que haver pelo menos um objeto na lista.

Fonte: http://cleveralias.blogs.com/thought_spearmints/2005/02/creating_an_arr.html

Edit: troquei 0 por list.size.

V

Bruno Laturner:
Achei uma solução

return list.toArray((T[]) Array.newInstance(list.iterator().next().getClass(), 0));

Obs: tem que haver pelo menos um objeto na lista.

Fonte: http://cleveralias.blogs.com/thought_spearmints/2005/02/creating_an_arr.html

Criativo, mas é uma puta gambiarra. :lol: :lol:

[Maldito type-erasure]

B
Pronto, uma versão sem gambiarra:
public static <T> T[] cortarNulos(T[] array)
   {
      if (array.length == 0)
         return array;

      ArrayList<T> list = new ArrayList<T>(array.length);
      for (T a : array)
      {
         if (a != null)
            list.add(a);
      }

      return list.toArray((T[]) Array.newInstance(array.getClass().getComponentType(), list.size()));
   }
E ainda funciona com arrays zero-dimensionais e arrays só com nulos, retorna zero-dimensionais do mesmo tipo, do jeito que queria.
V
Bruno Laturner:
Pronto, uma versão sem gambiarra:
public static <T> T[] cortarNulos(T[] array)
   {
      if (array.length == 0)
         return array;

      ArrayList<T> list = new ArrayList<T>(array.length);
      for (T a : array)
      {
         if (a != null)
            list.add(a);
      }

      return list.toArray((T[]) Array.newInstance(array.getClass().getComponentType(), list.size()));
   }
E ainda funciona com arrays zero-dimensionais e arrays só com nulos, retorna zero-dimensionais do mesmo tipo, do jeito que queria.

Realmente. Essa parece ser a solução certa. :)

Criado 30 de janeiro de 2009
Ultima resposta 30 de jan. de 2009
Respostas 25
Participantes 8