Genéricos - Não é apenas em tempo de compilação?

3 respostas
V

Pelo o que eu estava estudando a aplicação de genéricos é para "type safe", onde é possível verificar erros em tempo de compilação.

Ou seja, após compilado os dois códigos seriam iguais:

List<String> lista1 = new ArrayList<String>();
List lista2 = new ArrayList();

Porém, esbarrei com a seguinte questão em um simulado:

import java.util.*;

class test
{
	public static void main (  String [] args )
	{
		Map < Integer , String > map = new LinkedHashMap < Integer , String > (); //1
		Map < Integer , String > sap = new HashMap < Integer , String > (); //2
		populate( map );
		populate( sap );
		System.out.println( map.get(1) + sap.get(1) );
	}

	static void populate ( Map  m )
	{
		for ( int i = 0 ; i < 10 ; i++ )
		{
			m.put(i,i);
		}
	}

}

A resposta é "Runtime error".

Mas se alterarmos as linhas 1 e 2 para o código abaixo funciona:

Map map = new LinkedHashMap ();
		Map sap = new HashMap();

Os dois após compilados não seriam iguais? Por que um funciona e o outro não?

3 Respostas

C

Olá victor_vs,

Perceba que o primeiro código, quando você compilou, gerou um warning (unchecked), indicando que PODE ocorrer uma exceção em tempo de execução (ClassCastException). O problema é que os objetos map e sap foram declarados para receber um inteiro e uma String no método put (e vários outros métodos de Map que utiliza os tipos genéricos). Do ponto de vista da compilação, a chamada do método populate(Map) gera warning porque, para esse método, ele recebe um Map de qualquer coisa (porque não foi especificado o tipo). E qualquer coisa nem sempre é <Integer, String>. E a implementação dele realmente quebra isso, colocando dois inteiros.

Quando você tira a declaração de tipo genérico dos mapas, o mapa pode caber qualquer combinação de objetos, de qualquer classe, inclusive <Integer, String>, por isso não dá erro (se eu não me engano, também vai dar um warning, de uso de “raw type”).

Portanto, o primeiro código (com tipos genéricos) não é exatamente a mesma coisa que o segundo, ele é um mapa (tal qual o mapa sem tipo genérico), porém são colocadas verificações de tipos no local onde esses tipos genéricos são usados (que é a razão do ClassCastException). O código compilado não pode ser igual, senão não teria sentido usar Generics.

Espero gerar alguma discussão aqui, porque Generics é sempre tema de debate, e tem coisas internas que acontecem quando se usa isso que eu também não sei como funciona :slight_smile:

Até mais,

V

Obrigado pela resposta CD1! :wink:

É verdade... os dois códigos compilados tem diferença sim. Pensando melhor aqui lembrei de umas das funcionalidades de Genéricos: não precisar fazer cast ao utilizar método que retorne um tipo genérico.

Provavelmente o método get para "Map < Integer , String >" deve ficar algo parecido com:

public String get&#40;Object key&#41; &#123;
        Object obj = null;
        
        // &#40;...&#41; pega o objeto de acordo com a chave
        
        return &#40;String&#41; obj;
    &#125;

Naquela questão, na hora que vai retornar a JVM lança um ClassCastException porque tenta converter um objeto Integer em String.

No caso de não usar genéricos funciona porque ficaria:

public Object get&#40;Object key&#41; &#123;
        Object obj = null;
        
        // &#40;...&#41; pega o objeto de acordo com a chave
        
        return obj;
    &#125;
U

exato, os generics são traduzidos para typecasts depois de compilado …
o que é apenas em tempo de compilação, é a informação de que tipo foi utilizado para parametrizar :smiley:

Criado 19 de agosto de 2006
Ultima resposta 19 de ago. de 2006
Respostas 3
Participantes 3