Pergunta

Corri para uma situação hoje onde Java não foi invocar o método que eu esperava - Aqui é o caso de teste mínimo: (Sinto que isso parece planejado - o cenário de 'mundo real' é substancialmente mais complexo e marcas muito mais sentido a partir de um "por que diabos você faria que ?" ponto de vista.)

Eu estou interessado especificamente em por que isso acontece, eu não me importo com sugestões redesenho. Tenho a sensação este é em Java Puzzlers, mas eu não tenho a minha cópia acessível.

Veja a questão específica em louva dentro Teste .getValue () abaixo:

public class Ol2 {  

    public static void main(String[] args) {  
        Test<Integer> t = new Test<Integer>() {  
            protected Integer value() { return 5; }  
        };  

        System.out.println(t.getValue());  
    }  
}  


abstract class Test<T> {  
    protected abstract T value();  

    public String getValue() {  
        // Why does this always invoke makeString(Object)?  
        // The type of value() is available at compile-time.
        return Util.makeString(value());  
    }  
}  

class Util {  
    public static String makeString(Integer i){  
        return "int: "+i;  
    }  
    public static String makeString(Object o){  
        return "obj: "+o;  
    }  
} 

A saída desse código é:

obj: 5
Foi útil?

Solução

Não, o tipo de valor não está disponível em tempo de compilação. Tenha em mente que javac só irá compilar uma cópia do código a ser usado para todos os possíveis T do. Dado que, o único tipo possível para o compilador para uso em seu método getValue () é Object.

C ++ é diferente, porque ele acabará por criar várias versões compiladas do código, conforme necessário.

Outras dicas

Como a decisão sobre o que makeString() a utilização é feita em tempo de compilação e, com base no fato de que T pode ser qualquer coisa, deve ser a versão Object. Pense nisso. Se você fez Test<String> ele teria que chamar a versão Object. Como tal, todas as instâncias do Test<T> usará makeString(Object).

Agora, se você fez algo como:

public abstract class Test<T extends Integer> {
  ...
}

coisas poderiam ser diferentes.

Josh Bloch do Effective Java tem uma excelente discussão esclarecer a confusão que surge porque expedição funciona de forma diferente para sobrecarregado vs substituído (em uma subclasse) métodos. Seleção entre sobrecarregado métodos --- o assunto desta questão --- é determinado em tempo de compilação; seleção entre substituído métodos é feito em tempo de execução (e, portanto, obtém conhecimento do tipo específico do objeto.)

O livro é muito mais clara do que o meu comentário: See "Item 41: Use sobrecarga judiciosamente"

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top