Generics e herança

11 respostas
sergiolopes

Pessoal, uma dúvida de generics:

Tenho um método assim:

public void metodo(Class<? extends Action> action) {
    ...
}

Porque, dentro dele, eu nao posso fazer isso

Class&lt;Action&gt; action2 = action;

sem fazer o cast para Class<Action> ??

11 Respostas

renatosilva

Porque Java é feio! :smiley: Eu também reparei isso. Acho que é um motivo puramente técnico e não arquitetutal, orientação-a-objetal, filosofal etc. Acho que quem entende de bytecode pode explicar melhor, e eu não sou um deles :smiley:

_fs

O upperbound wildcard define que o parâmetro deve ser um subclasse da classe definida.

Mais aqui:
http://java.sun.com/developer/technicalArticles/J2SE/generics

Mas, me diga, por que você precisa fazer esse cast dentro do método?

sergiolopes

No método real, eu pego esse atributo Action e coloco ele num List<Class>. Eu não consigo colocar o atributo action daquele metodo sem fazer o casting.

O que eu nao entendo é: se meu metodo recebe Class de qualquer coisa que extende Action, porque não posso usar esse objeto como Class?

Fazendo o casting, tenho segurança de que sempre estará correto?? Não existe a menor chance de dar ClassCastException, certo?

O eclipse dá warning nesse casting… não quero ficar jogando @SuppressWarnings(“unchecked”) pelo meu código…

louds

Que eu saiba não existe covariancia por tipo genérico.

Ou seja Class<Integer> não é um tipo de Class<Object>.

sergiolopes

Certo… nao automaticamente

Mas estou certo em dizer que se fizer um casting de Class para Class isso sempre vai estar correto?? Ou existe alguma possibilidade de dar ClassCastException?

louds

Sérgio, um cast entre parâmetros de um mesmo tipo genérico não deveria dar class cast exception jamais.

Não testei, mas o seguinte código não deveria larçar exceptions:

Class<JButton> a = JButton.class;
Class<URL> u = (Class<URL>)a;

Lembre que temos erasure, e no final das contas vai ser tudo Class mesmo.

sergiolopes

eu tenho aquela lista de List<Class>

quando eu fizer: Action a = list.get(0).newInstance();
isso nao pode dar ClassCastException?

T

Só brincando um pouco. Este código compila OK mesmo com -Xlint:unchecked.

import java.util.*;

class Action {
    public void acao() {
        System.out.println (getClass().getName());
    }
}

class UmaAction extends Action {
    @Override public void acao() {
        System.out.println ("xxx:" + getClass().getName());
    }
    public void outraCoisa() {
    }
}


class TestGenerics {
    
    /** Note que isto é uma lista de Class, não de Action */
    List < Class  < ? extends Action > > clActions = new ArrayList < Class  < ? extends Action > >();
    
    public void metodo(Class < ? extends Action > clAction) {
        clActions.add (clAction);
        
        try {
            Action act = clAction.newInstance();
            act.acao();
        } catch (InstantiationException ex) {
            ex.printStackTrace();
        } catch (IllegalAccessException ex) {
            ex.printStackTrace();
        }
    }
    
    public void listaClActions () {
        for (Class < ? extends Action > clAct : clActions) {
            System.out.println ("--->" + clAct.getName());
        }
    }
    
    public void executaClActions () {
        for (Class < ? extends Action > clAct : clActions) {
            try {
                Action act = clAct.newInstance();
                act.acao();
            } catch (InstantiationException ex) {
                ex.printStackTrace();
            } catch (IllegalAccessException ex) {
                ex.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        TestGenerics tg = new TestGenerics();
        tg.metodo (Action.class);
        tg.metodo (UmaAction.class);
        tg.listaClActions();
        tg.executaClActions();
    }
}
sergiolopes

nossa, legal isso
acho q agora entendi o negocio

vc nao trata como Class, mas sempre como Class<? extends Action>… realmente isso faz mais sentido.
e vc nao precisa fazer cast em lugar nenhum.

eu me confundo mto com generics… o fato de ser em tempo de compilacao e nao de execucao as vezes confunde um pouco as possibilidades

valeu pessoal!

T

É pena que não baixei ainda o compilador do C# 2.0. Daria para comparar as implementações de generics entre o C# e o Java.

Se não me engano, essa coisa esquisita de “? extends Action” seria em C#:

using System.Generics.Collections;
...
IList < Type < T > > clActions = new ArrayList < Type < T > > () 
    where T : Action;

onde “IList” é a interface java.util.List, e “Type” é a java.lang.Class.

Em C# dá chabu misturar a classe System.Generics.ArrayList < T > e a classe System.ArrayList; não são água e óleo, mas não é recomendado misturar as duas .
Em Java são a mesma classe ( java.lang.ArrayList <E> ).

Gerson

“Class<? extends Action> action”
isso quer dizer que vc pode atribuir à variavel action uma instancia de Class desde ela corresponda a uma subclasse de Action, incluindo a propria Action.

“Class<Action> action2”
isso quer dizer que vc pode atribuir à variavel action2 uma instancia de Class desde que ela corresponda a classe Action.

Agora imagina se vc passasse um Class<SubClasseDeAction> no parametro do metodo (usando um Class.forName(…))! Não seria mais verdade que action2 “aponta” para uma instancia de Class que corresponde a uma Action ( Class<Action> ), certo?! E cuidado, pois se vc forçar o cast, provavelmente o seu compilador irá lançar um warning dizendo que vc tá jogando o generics pro espaço!! Isso quer dizer q não ha mais nenhuma garantia em tempo de compilacao (unsafe), sendo ela por sua conta apenas! :wink:

Espero ter ajudado!

Criado 16 de julho de 2005
Ultima resposta 18 de jul. de 2005
Respostas 11
Participantes 6