Generics

15 respostas
sech777

Pessoal, boa noite!

Por quê a seguinte declaração compila sem avisos?

List l = new ArrayList<Integer>();

Equanto que está abaixo compila com avisos?

List<Integer> l = new ArrayList();

Nesses casos, estamos misturando código antigo com código genérico, então deveria emitir um aviso em ambos os casos certo?!

No entanto, o primeiro caso apenas emiti um aviso quando se tenta adicionar algo ao arraylist pela chamada “l.add();”, caso contrário não.

Obrigado,

Filipe

15 Respostas

WendersonLP

Bem no primeiro caso voce esta criando uma instancia, como voce nao esta restringindo o tipo de entrada
dos dados que serao permitidos nessa lista o tipo por default sera Object, do mesmo tipo, lembre-se de que
a classe ArrayList ela implementa a interface List. Ja no segunda caso voce esta definindo o tipo de dado que
voce, so que voce esta definindo isso no objeto de List, mas na instancia voce nao esta fazendo essa restrica,
que dese ser do mesmo tipo, por isso esta ocorrendo erro na usa aplicacao.

ViniGodoy

O java permite conversões imediatas de listas genéricas para não genéricas, por questões de compatibilidade. Essa conversão também é insegura, mas seria péssimo que o java entupisse o código de warnings num código legado, por exemplo. Por isso a ausência do warning.

Porém, no segundo caso, você está tentando fazer o contrário, você explicitamente disse que quer trabalhar com generics, conforme indica o tipo de sua variável, mas negligenciou esse fato atribuindo a ele uma lista comum. Essa lista, se não tivesse sido criada aí, poderia até ter outros tipos de objeto dentro, o que violaria todo o propósito de usar generics em primeiro lugar.

euprogramador

Em uma palestra que houve aqui em brasília, um dos desenvolvedores da SUN, ao apresentar novidades do java 7 informou que irá ser modificada a forma de usar generics,

Hoje como dito se você fizer assim dá avisos.

List<Integer> lista = new ArrayList();

mas parece redundância demais ter que informar o tipo na interface e na construção do objeto:

List<Integer> lista = new ArrayList<Integer>();

Portanto eles estarão modificando para que fique assim:

List<Integer> lista = new ArrayList<>();

um syntax sugar.

ViniGodoy

Essa notação ficou conhecida como diamond notation.

sergiotaborda

sech777:
Pessoal, boa noite!

Por quê a seguinte declaração compila sem avisos?

List l = new ArrayList<Integer>();

Equanto que está abaixo compila com avisos?

List<Integer> l = new ArrayList();

A primeira vc está atribuindo algo especifico a algo menos especifico. Logo é aceite. É como atribuir int a long.
A segunda vc está atribuindo algo menos especifico a algo especifico. Isso pode ter problemas ,como atribuir long a int.
A atribuição é feita, mas o compilador gera um aviso exatamente para que vc tenha consciência que ali ha um problema.
A maior parte das vezes isso é um erro. Quando vc tiver a certeza que não é vc pode escrever assim:

List<Integer> l = new ArrayList< Integer >();

que é o correto, mas se isso não for possivel por alguma razão , vc pode escrever

@SuppressWarnings("unchecked") List<Integer> l = new ArrayList();

o SuppressWarnings é para ser usado na linha da atribuição e não no método inteiro. Atenção a isso.

ViniGodoy

De qualquer forma, nesse caso você pode violar a condicional:

List x = new ArrayList<Integer>(); x.add(new Thread()); //nenhum erro

Conforme o gosling explicou no artigo sobre generics, esse argumento não é válido no caso de listas por causa disso. Foi feito mesmo por compatibilidade.

euprogramador

eu acho generics uma gambiarra na linguagem java, visto que internamente o compiler transforma em cast.

sergiotaborda

Se vc acha que é gambiarra seja coerente consigo mesmo e não use. Afinal não trazem nenhuma qualidade ao código. :twisted:

euprogramador

achar que é gambiarra não quer dizer que não goste, acho que temos um benefício muito bom que é a checagem em tempo de compilação.

outra coisa que acho interessante é o uso de métodos mágicos:

public class Fabrica{
    private Map<String,Object> mapa = new HashMap<String,Object>();

    public void registrar(String key, Object o){
        mapa.put(key,o);
    }

    public <T> T construir(String obj){
        return (T) mapa.get(obj);
    }
}

Fabrica f= new Fabrica();
f.registrar("numero",new Long(1));
f.registrar("texto","aa");

String texto = f.recuperar("texto");
Integer numero = f.recuperar("numero");
renamed

Não acho que seja uma gambiarra!

Para mim é uma forma de garantir que sua lista ou map ou seja lá o que for está condizente!

List lista = new ArrayList(); lista.add("oi"); lista.add(33); lista.add(false); lista.add('d'); lista.add(new Color(0,0,0,0));

Como pode ver a lista acima está toda embananada… aceita qualquer valor que eu quiser add… isso é um passo pro abismo de bugs que isso pode causar!

List<String> lista = new ArrayList<String>(); lista.add("oi"); lista.add(33); //opa! deu erro! lista.add(false); //opa! deu erro! lista.add('d'); //opa! deu erro! lista.add(new Color(0,0,0,0)); //opa! deu erro!

E sobre métodos “mágicos”, não acho que seja uma boa idéia informar “Object” na sua construção… a ideia principal é garantir que os argumentos serão válidos!

ok? =)

euprogramador

Sobre os métodos mágicos, eles somente seriam úteis em contextos, onde não seria mais necessário fazer o cast visto que o método faz o cast antes já retornando o objeto da forma solicitada, conforme disse não quer dizer que não gosto dos generics, o proposito deles foi exatamente deixar o código mais coeso e evitar os problemas que tinhamos quando esquecia de fazer cast.

ViniGodoy

Acho que o que o compilador faz ou deixa de fazer pouco importa. O que importa é as consequencias disso para a linguagem. Os problemas gerados pela estratégia usada pelo Java são o type erasure, a impossibilidade de determinar o tipo de T (mesmo via reflexão), e a possibilidade de fazer conversões unsafe, por compatibilidade. Para a absurda maioria dos casos, essas coisas nunca chegam a incomodar.

Em algumas linguagens, como o C++, recursos fantásticos são criados unicamente pelo trabalho do compilador, que altera completamente o código antes de compila-lo. Um exemplo disso é o que é feito com os templates, que possibilitam inclusive a escrita de funções que rodem em compile time.

sech777

Obrigado pelas opiniões pessoal!

sergiotaborda

euprogramador:
achar que é gambiarra não quer dizer que não goste, acho que temos um benefício muito bom que é a checagem em tempo de compilação.

outra coisa que acho interessante é o uso de métodos mágicos:

public class Fabrica{
    private Map<String,Object> mapa = new HashMap<String,Object>();

    public void registrar(String key, Object o){
        mapa.put(key,o);
    }

    public <T> T construir(String obj){
        return (T) mapa.get(obj);
    }
}

Fabrica f= new Fabrica();
f.registrar("numero",new Long(1));
f.registrar("texto","aa");

String texto = f.recuperar("texto");
Integer numero = f.recuperar("numero");

Isso não são “metodos mágicos” isso é o uso da covariancia. uma coisa que foi introduzida ao mesmo temo que genéricos.
Covariancia é algo que não depende dos genéricos, mas é princiaplemente util com ele.
Esses métodos são chamados “métodos de retorno covariante” ou simplesmente “métodos covariantes”

o problema do seu codigo é que se vc fizer

Integer numero = f.recuperar("texto"); String texto = f.recuperar("numero");

Dá erro. mesmo com uma assinatura mais forte como

public class Fabrica{
    private Map<String,Object> mapa = new HashMap<String,Object>();

    public void registrar(String key, Object o){
        mapa.put(key,o);
    }

    public <T> T construir(String obj, Class<T> type){
        return type.cast(mapa.get(obj));
    }
}

O mesmo erro ainda é possivel. Veja, isto não é culpa dos genérics, nem do java. Isto é simplesmente culpa de um mau design da classe. Se vc fizer isso em java 1.4 terá o mesmo problema.

ViniGodoy

Um exemplo de covariância sem genéricos pode ser o método clone. A assinatura dele em Object é assim:

public Object clone();

Mas na sua classe você pode defini-lo assim:

public class SuaClasse implements Cloneable { public SuaClasse clone() { return (SuaClasse) super.clone(); } }

Permitindo que quem use sua classe não precise fazer casts:

SuaClasse sc = outroSc.clone();
Criado 16 de dezembro de 2009
Ultima resposta 17 de dez. de 2009
Respostas 15
Participantes 6