Dúvida genéricos

30 respostas
V

Olá pessoal estava testando alguns métodos envolvendo genéricos e me deparei com a seguinte situação:

//tenho este método que retorna um array lista de integer
public List<? extends Number> criarLista3() {
		
   return new ArrayList<Integer>();
}
	
public static void main(String[] args) {
	
  MetodosGenericos4 m = new MetodosGenericos4();
  //aqui está a dúvida estou fazendo um casting de ArrayList<Integer> para ArrayList<Number>
  //funcionou mas ao meu ver estaria errado pois não se pode fazer ArrayList<Number> = ArrayList<Integer>, tudo bem eu fiz o casting, mas o meu retorno está referenciando um ArrayList<Integer> isso não daria um erro????
  ArrayList<Number> lista1 = (ArrayList<Number>)m.criarLista3();
  lista1.add(new Float(12));
  System.out.println(lista1);
}

Ao meu ver deu certo esse casting por causa do seguinte tipo de retorno no método

public List<? extends Number> criarLista3() { ... }
//com isso eu posso fazer o casting do subtipo para o supertipo na hora de chamar o método

Estou correto nesta afirmação???
Se alguém puder me ajudar, ficarei grato.
Vinícius.

30 Respostas

LPJava

nao pode cara, vc jamais pode ter objetos diferentes dentro de um tipo generico, e se extends algo com generico nao pode adicionar

dar uma olhada nesse meu post.

V

Obrigado pela ajuda, mas aqui no eclipse esse código compilou.

LPJava

nao cheguei a testar o codigo, porem estava falando desta parte do comentario:

realmente isso eh invalido.

V

Eh, ta complicado, estava fazendo uns testes aqui e teve uma coisa que me deixou mais confuso
porque isto funciona???

ArrayList<? extends Number> lista2 = new ArrayList<Number>();  
  //casting de ArrayList<Number> para ArrayList<String> porque isso funciona????
  //a regra para casting de ArrayList é diferente da regra comum para casting de objetos????
  ArrayList<String> lista4 = (ArrayList<String>)lista2;
  lista4.add("1");
  lista4.add("2");
  lista4.add("3"); 
  System.out.println(lista4);
nbluis

E funciona ?

Eu não consegui compilar …

LPJava

eu tb nao conseguir compilar nao. As regras sao claras, para entender conjuntos + genericos tem que ler as regras, nao basta codificar e tentar entender na marra, tendo como base os erros do compilador.

V

funciona aqui no eclipse…
vou tentar compilar no java normal…
mas msm assim no eclipse eu configurei ele para compilar no java 6…

V

eu estou entendendo as regras…
mas quando vou compilar o resultado infrige a regra ai fica complicado…

V

compilei no java normal e ai não funcionou msm como eu esperava…
mas ai como que fica…
porq o eclipse faz isso???..
devo usar um outro editor para trabalhar com java???..

nbluis

Cara, aqui num ta compilando não. rsrs

java version "1.6.0_11"
Java(TM) SE Runtime Environment (build 1.6.0_11-b03)
Java HotSpot(TM) Client VM (build 11.0-b16, mixed mode, sharing)
Teste.java:8: inconvertible types
found   : java.util.ArrayList&lt;capture#469 of ? extends java.lang.Number&gt;
required: java.util.ArrayList&lt;java.lang.String&gt;
                ArrayList&lt;String&gt; lista4 = (ArrayList&lt;String&gt;) lista2;
                                                               ^
1 error
LPJava

vmsb11:
funciona aqui no eclipse…
vou tentar compilar no java normal…
mas msm assim no eclipse eu configurei ele para compilar no java 6…

genericos eh recurso a partir do java 1.5 e na 1.6 nao houve alteracoes em relacao as regras de comportamento com genericos, o fato eh simples nao colocar Uvas na caixa de Maças.

flw…

LPJava

e o compilador ai foi bem objetivo em dizer aonde ta o erro, associado as regras vc mata a questao…

veja o link que passei, tem uma boa base sobre o assunto.

V

eh aqui deu o mesmo erro compilando no console…
amigo LPJava eu ja tinha lido o seu post, muito bom msm…
eu estou entendendo as regras mas fiquei na dúvida por causa desse teste que eu fiz no eclipse ontem…
pelo oque eu estou vendo o eclipse que está fazendo uma confusão…

V

testei no netbeans e realmente não funcionou…
alguem sabe me dizer porq o eclipse alguns códigos por mais que se pareçam bizarros eles compilam e funcionam???..
obs: estou usando o MyEclipse v 5.5

nbluis

Mas o debate é legal.

Olha um comportamento engraçado que eu testei agora.

public static void main(String[] args) {
		ArrayList&lt;Cachorro&gt; lista2 = new ArrayList&lt;Cachorro&gt;();
		lista2.add(new Cachorro("Snoopy"));
		ArrayList&lt;String&gt; lista4 = ((ArrayList&lt;String&gt;) (Object) lista2);
		lista4.add("1");
		lista4.add("2");
		lista4.add("3");
		System.out.println(lista4);
	}

Suponha que voce tenha uma classe cachorro ok ?
A saida:

[Cachorro@addbf1, 1, 2, 3]

Se eu faço o seguinte:

public static void main(String[] args) {
		ArrayList&lt;Cachorro&gt; lista2 = new ArrayList&lt;Cachorro&gt;();
		lista2.add(new Cachorro("Snoopy"));
		ArrayList&lt;String&gt; lista4 = ((ArrayList&lt;String&gt;) (Object) lista2);
		lista4.add("1");
		lista4.add("2");
		lista4.add("3");
		lista4.get(0).toString();
		System.out.println(lista4);
	}

Colocando uma chamada a lista4.get(0) que é o objeto Cachorro.
Acontece um erro:

Exception in thread "main" java.lang.ClassCastException: Cachorro cannot be cast to java.lang.String
	at Teste.main(Teste.java:12)

Se chamo qualquer outro funciona beleza.

Não fui muito a fundo, mas parece que como na implementação do ArrayList ele armazena em um Object[] dentro da lista ele continua trabalhando mesmo com um objeto inválido.
E quando fazemos o get, por fazer referência ao generico String ele tenta fazer o cast e quebra tudo.

As vezes a gente encontra umas coisas engraçadas estudando estes exemplos.

C_ar_Rodolfo

Ué aquele primeiro codigo funcionou ake no NetBeans…
Não ta dando certo pq ele converteu pra ArrayList?

C_ar_Rodolfo

Isso ake tbm é valido?

ArrayList<? extends Number> c = new ArrayList();

V

Isto funciona mas vc não poderá adicionar nada na lista…

V

nbluis:
Mas o debate é legal.

Olha um comportamento engraçado que eu testei agora.

public static void main(String[] args) {
		ArrayList&lt;Cachorro&gt; lista2 = new ArrayList&lt;Cachorro&gt;();
		lista2.add(new Cachorro("Snoopy"));
		ArrayList&lt;String&gt; lista4 = ((ArrayList&lt;String&gt;) (Object) lista2);
		lista4.add("1");
		lista4.add("2");
		lista4.add("3");
		System.out.println(lista4);
	}

Suponha que voce tenha uma classe cachorro ok ?
A saida:

[Cachorro@addbf1, 1, 2, 3]

Se eu faço o seguinte:

public static void main(String[] args) {
		ArrayList&lt;Cachorro&gt; lista2 = new ArrayList&lt;Cachorro&gt;();
		lista2.add(new Cachorro("Snoopy"));
		ArrayList&lt;String&gt; lista4 = ((ArrayList&lt;String&gt;) (Object) lista2);
		lista4.add("1");
		lista4.add("2");
		lista4.add("3");
		lista4.get(0).toString();
		System.out.println(lista4);
	}

Colocando uma chamada a lista4.get(0) que é o objeto Cachorro.
Acontece um erro:

Exception in thread "main" java.lang.ClassCastException: Cachorro cannot be cast to java.lang.String
	at Teste.main(Teste.java:12)

Se chamo qualquer outro funciona beleza.

Não fui muito a fundo, mas parece que como na implementação do ArrayList ele armazena em um Object[] dentro da lista ele continua trabalhando mesmo com um objeto inválido.
E quando fazemos o get, por fazer referência ao generico String ele tenta fazer o cast e quebra tudo.

As vezes a gente encontra umas coisas engraçadas estudando estes exemplos.

Engraçado msm esses exemplos… por isso é válido algumas vezes abrir o código fonte e ver como que é a implementação das coleções no java…

C_ar_Rodolfo

Ah vlw ^^
E quanto ao primeiro codigo?
Aki deu certim, estranhissimo XD

V

estranho msm…
aqui o primeiro código funcionou no eclipse…
mas no java normal não funcionou…

LPJava

pessoal, veja as linhas abaixo:

sao diferentes o cast que está sendo feito.

C_ar_Rodolfo

ArrayList lista1 = (ArrayList)m.criarLista3();

Esse cast não deveria funcionar…
Pelo menos eu acho né ^^

LPJava

C?ar.Rodolfo:
ArrayList lista1 = (ArrayList)m.criarLista3();

Esse cast não deveria funcionar…
Pelo menos eu acho né ^^

ele nao vai mesmo, se funcionasse eu poderia colocar qualquer coisa no meu array do tipo e isso nao eh permitido.

V

Compilei esse código no console do java msm

import java.util.*;
public class Teste {

  public List<? extends Number> criarLista() {
		
    return new ArrayList<Integer>();
  }

  public static void main(String[] args) {
		
    Teste m = new Teste();
    ArrayList<Number> lista1 = (ArrayList<Number>)m.criarLista();
    lista1.add(new Float(12));
    System.out.println(lista1);
  }
}

E esse casting funcionou sim!!!..deu uma warning mas funcionou…

LPJava
e pq ele nao funcionaria? nao vejo nada de errado nele. vc tem um metodo que retornar um
List<? extends Number>
ou qualquer coisa que seja filho de Number.
ArrayList<Number> lista1 = (ArrayList<Number>)m.criarLista();

aqui vc declaracou uma lista que vai receber o resultado o metodo que retorna um Number.

E depois vc adicionou um objeto Float no seu conjunto lista1 que está referenciado ao metodo que retorna
List<? extends Number>
ele imprime com warnings pq a seguranca ai foi para o saco, ja que vc quebrou ela. isso ai eh o mesmo disso:
public class TestGenerics {

	/**
	 * @param args
	 */
ArrayList<Number> lista = new ArrayList<Number>();
			lista.add(new Float(12));
			lista.add(new Integer(10));
			System.out.println(lista);

}
ja isso aqui nao compila:
public class TestGenerics {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ArrayList<Number> lista = new ArrayList<Float>();
			lista.add(new Float(12));

	}

}
V

sim…isso ai eu ja entendi…
oque me deixou em dúvida é porq no retorno do método

return new ArrayList<Integer>();

retornei um array list de integer(até ai tudo bem, faz parte da regra de retorno que defini) mas na hora que eu faço casting

isso no meu entendimento está passando um array list de integer para number certo???
então oque eu entendi quando o tipo de retorno do método é <? extends ClasseX> eu posso fazer um casting de um subtipo para um supertipo na hora de chamar o método como eu fiz, está certo???
muito obrigado pela ajuda LPJava.

LPJava

vmsb11:
sim…isso ai eu ja entendi…
oque me deixou em dúvida é porq no retorno do método

return new ArrayList<Integer>();

retornei um array list de integer(até ai tudo bem, faz parte da regra de retorno que defini) mas na hora que eu faço casting

isso no meu entendimento está passando um array list de integer para number certo???
então oque eu entendi quando o tipo de retorno do método é <? extends ClasseX> eu posso fazer um casting de um subtipo para um supertipo na hora de chamar o método como eu fiz, está certo???
muito obrigado pela ajuda LPJava.

qualquer um que passe no teste É-UM vc pode retornar quando se tem X extends Y.

V

entendi tudo LPJava…obrigado pela ajuda…
só tenho mais uma dúvida com relação a casting em genéricos…

quando eu faço:

ArrayList<Integer> lista1 = new ArrayList<Integer>();
  //esse tipo de casting não é permitido né????
  ArrayList<Number> lista2 = (ArrayList<Integer>)lista1;

mas quando eu declaro algo do tipo:

ArrayList<? extends Number> lista1 = new ArrayList<Integer>;
   //esse casting é permitido ????
   ArrayList<Float> lista2 = (ArrayList<Float>)lista1;

estou correto nisso???..

LPJava

yes.

Criado 29 de dezembro de 2009
Ultima resposta 31 de dez. de 2009
Respostas 30
Participantes 4