Type casting no objeto

4 respostas
hlegius

Salve povo !

Tenho uma classe definida por uma interface. Esta classe (ou estas, por isso a interface) é injetada em um "Autenticador" onde eu via Reflection pego o nome da tal classe para fazer uma factory ali.
O problema é que o Eclipse fica com uma mensagem de aviso sobre meu type casting e eu como bom iniciante em Java não sei o que poderia fazer para torná-lo correto. Segue:

public class Autentica{
    private Class<Authentication> autenticado;
    
    public boolean login(Authentication autenticado) {        
        this.autenticado = (Class<Authentication>)autenticado.getClass();
        // Erro retornado pelo eclipse: [b]Type safety: Unchecked cast from Class<capture#1-of ? extends Authentication> to Class<Authentication>[/b] 
        
        try {
            return this.authenticateFor(this.autenticado.getName());
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        
        return false;
    }
// ....
}

Authentication é minha interface
this.autenticado é o tal objeto recebido, no caso uma classe que implemente a interface - e é dela que eu quero saber o nome

Eu realmente não sei o que está zuado para que possa tornar esse casting inseguro :/ Dicas ?

Abraços !

4 Respostas

andeb

Essa warning é meia estranha, me corrijam caso eu fale besteira, pelo que entendi de acordo com a domentação, é que o método getClass de Object retorna a class do objeto definido em runtime. Sendo assim, acredito eu, não seria lógico usar polimorfismo para quando dar um getClass() em CharSequence retornar um objeto Class quando na verdade eu possuo uma instância de String definida em runtime, daí o que o compilador faz é usar um wildcard com extends. O que é definido na documentação é que o compilador retorna sempre um <? extend X> onde X é o tipo da variável de referência. Ex.:

Object obj = new Object(); Class<Object> clazz = obj.getClass(); // não compila, retorna um Class<? extends Object>

Para resolver seria só a declaração do atributo para:

private Class<? extends Authentication> autenticado;

ou adicionar uma supress warning, já que nesse caso é seguro:

@SuppressWarnings("unchecked") public boolean login(Authentication autenticado) { this.autenticado = (Class<Authentication>)autenticado.getClass();

Até agora não achei um bom motivo pro java acusar como warning isso, acho mais uma gambizera do tipo genéricos que foi pensando pra ser utilizados em coleções que se esqueceu do resto.

Sei que só enrolei e que não respondi sua dúvida mas talvez ajude pra algo isto :smiley:
Creio que a dúvida está em como utilizar corretamente o wildcard.

Caso falei muita alguma besteira, por favor me corrijam. :slight_smile:

B

Tô meio sem cabeça pra pensar do por que ele dá um warning, mas o que estou estranhando é por que você está usando Reflection e Generics. O que esse método deve fazer?

M

Realmente ele retornar o tipo em runtime. Por exemplo, se você tem um método:

void mostraClasse(Object o) {
    System.out.println(o.getClass().getName());
}

Esse trecho vai fazer o que se espera que ele faça, independentemente do tipo de argumento que você passar:

mostraClasse(""); // java.lang.String
mostraClasse(2); // java.lang.Integer

E isso só funciona porque o método getClass() de Object não retornar Object.class e sim o objecto Class de runtime (um Class de algo que estende Object, portanto um Class<? extends Object> ).

No seu caso, a mesma coisa ocorre: como a classe é definida em Runtime, ele não retornará Authentication.class e sim o objeto Class da implementação de Authentication (portanto, um Class<? extends Authentication> ).

Ok, mas e daí? Por que o cast gera um warning? Simples: porque um SeiLaOQue pode não ser o mesmo que um SeiLaOQue<? extends T>. Um exemplo: suponhamos que temos um List de Object. Perceba que isso pode ser interpretado de duas formas:

  1. Uma lista que carrega qualquer objeto (podemos ter, por exemplo, uma String, um Integer e um Double).
  2. Uma lista que carrega um único tipo de objeto (por exemplo, uma lista que carrega somente Double, ou uma lista que carrega somente String que, por sua vez, são um tipo de Object).

Apesar de String estender Object, um List não estende um List justamente por causa do que foi citado acima: a lista de String carrega apenas Strings, enquanto uma lista de Object carrega qualquer tipo de objeto.

Uma consequência disso é que o código abaixo não compila:

List<Object> b = new ArrayList<String>();

Imagine que queremos um código para imprimir qualquer List de números:

static void imprimeSoma(List<Number> lista) {
	double soma = 0;                         
	for (Number n : lista) {                 
		soma += n.doubleValue();             
	}                                        
	System.out.println(soma);                
}

E depois chamamos assim:

List<Integer> b = new ArrayList<Integer>();
b.add(1);                                  
b.add(33);                                 
b.add(12);                                 
imprimeSoma(b);

O código acima não resolve nosso problema. Porque? Pois, como já dito, uma lista de inteiros não é uma lista de números. A solução? Fazer o mesmo que o getClass() faz: usar um wildcard.

static void imprimeSoma(List<? extends Number> lista) {
	double soma = 0;                                   
	for (Number n : lista) {                           
		soma += n.doubleValue();                       
	}                                                  
	System.out.println(soma);                          
}

Agora funciona. Entendeu o porque do warning? Porque uma Class não é, necessariamente um Class<? extends Authentication>.

Mais informações: http://java.sun.com/docs/books/tutorial/extra/generics/index.html

hlegius

andeb, Bruno Laturner e marcobiscaro2112,

Caramba ! Excelentes explicações, nem esperava por tantos detalhes nas explicações ! =D

Bruno Laturner,
Meu objetivo com ele seria :

. O método recebe a interface Authentication
. Descobre qual o nome da classe (pois eu conheço apenas a interface) em runtime
. Com o seu nome em mãos, define como ela será tratada. É factory :slight_smile:

marcobiscaro2112,
Ao que entendi, é inseguro eu definir Foobar pois pode acabar retornando-me algo inesperado sendo ideal deixar o tipo bem definido.

Vou ler o link que me passou para pegar mais possíveis detalhes =D

Muito obrigado galera :slight_smile:

Criado 20 de janeiro de 2010
Ultima resposta 21 de jan. de 2010
Respostas 4
Participantes 4