Estou testando este código:
interface Callee {
public void foo(Object o);
public void foo(String s);
public void foo(Integer i);
}
class CalleeImpl implements Callee
public void foo(Object o) {
logger.debug("foo(Object o)");
}
public void foo(String s) {
logger.debug("foo(\"" + s + "\")");
}
public void foo(Integer i) {
logger.debug("foo(" + i + ")");
}
}
Callee callee = new CalleeImpl();
Object i = new Integer(12);
Object s = "foobar";
Object o = new Object();
callee.foo(i);
callee.foo(s);
callee.foo(o);
Isso imprime foo(Object o)
três vezes. Espero que a seleção do método leve em consideração o tipo de parâmetro real (não o declarado). Estou esquecendo de algo? Existe uma maneira de modificar este código para que ele seja impresso foo(12)
, foo("foobar")
e foo(Object o)
?
foo(Object)
. Em tempo de execução, a classe do objeto no qual o método é chamado determina qual implementação desse método é chamada, levando em consideração que pode ser uma instância de uma subclasse do tipo declarado que sobrescreve o método.Como mencionado antes, a resolução de sobrecarga é executada em tempo de compilação.
Java Puzzlers tem um bom exemplo para isso:
Quebra-cabeça 46: O Caso do Construtor Confuso
Este quebra-cabeça apresenta dois construtores confusos. O método principal invoca um construtor, mas qual? A saída do programa depende da resposta. O que o programa imprime ou é mesmo legal?
Solução 46: Caso do Construtor Confuso
... O processo de resolução de sobrecarga do Java opera em duas fases. A primeira fase seleciona todos os métodos ou construtores que são acessíveis e aplicáveis. A segunda fase seleciona o mais específico dos métodos ou construtores selecionados na primeira fase. Um método ou construtor é menos específico do que outro se pode aceitar quaisquer parâmetros passados para o outro [JLS 15.12.2.5].
Em nosso programa, ambos os construtores são acessíveis e aplicáveis. O construtor Confusing (Object) aceita qualquer parâmetro passado para Confusing (double []) , portanto, Confusing (Object) é menos específico. (Cada matriz dupla é um objeto , mas nem todo objeto é uma matriz dupla .) O construtor mais específico é, portanto, Confuso (double []) , o que explica a saída do programa.
Este comportamento faz sentido se você passar um valor do tipo double [] ; é contra-intuitivo se você passar null . A chave para entender este quebra-cabeça é que o teste para qual método ou construtor é mais específico não usa os parâmetros reais : os parâmetros que aparecem na invocação. Eles são usados apenas para determinar quais sobrecargas são aplicáveis. Uma vez que o compilador determina quais sobrecargas são aplicáveis e acessíveis, ele seleciona a sobrecarga mais específica, usando apenas os parâmetros formais: os parâmetros que aparecem na declaração.
Para invocar o construtor Confusing (Object) com um parâmetro nulo , escreva novo Confusing ((Object) null) . Isso garante que apenas Confuso (Objeto) seja aplicável. Mais geralmente, para forçar o compilador a selecionar uma sobrecarga específica, lance os parâmetros reais para os tipos declarados dos parâmetros formais.
fonte
A capacidade de despachar uma chamada para um método com base em tipos de argumentos é chamada de despacho múltiplo . Em Java, isso é feito com o padrão Visitor .
No entanto, como você está lidando com
Integer
s eString
s, não pode incorporar facilmente esse padrão (você simplesmente não pode modificar essas classes). Assim, um giganteswitch
em tempo de execução de objeto será sua arma de escolha.fonte
Em Java, o método a ser chamado (como em qual assinatura de método usar) é determinado em tempo de compilação, portanto, acompanha o tipo de tempo de compilação.
O padrão típico para contornar isso é verificar o tipo de objeto no método com a assinatura Object e delegar ao método com uma conversão.
Se você tiver muitos tipos e isso não for gerenciável, a sobrecarga de método provavelmente não é a abordagem certa, em vez disso, o método público deve apenas pegar Object e implementar algum tipo de padrão de estratégia para delegar a manipulação apropriada por tipo de objeto.
fonte
Tive um problema semelhante ao chamar o construtor certo de uma classe chamada "Parameter" que poderia receber vários tipos básicos de Java, como String, Integer, Boolean, Long, etc. Dado um array de objetos, quero convertê-los em um array de meus objetos Parameter chamando o construtor mais específico para cada Object na matriz de entrada. Eu também queria definir o parâmetro do construtor (Object o) que lançaria uma IllegalArgumentException. É claro que descobri que esse método está sendo invocado para cada objeto em meu array.
A solução que usei foi procurar o construtor por meio de reflexão ...
Nenhuma instância feia, instruções switch ou padrão de visitante necessário! :)
fonte
Java examina o tipo de referência ao tentar determinar qual método chamar. Se você quiser forçar o seu código, escolha o método 'certo', você pode declarar seus campos como instâncias do tipo específico:
Você também pode lançar seus parâmetros como o tipo do parâmetro:
fonte
Se houver uma correspondência exata entre o número e os tipos de argumentos especificados na chamada do método e a assinatura do método de um método sobrecarregado, esse é o método que será chamado. Você está usando referências de objeto, portanto, java decide em tempo de compilação que, para o parâmetro de objeto, há um método que aceita diretamente o objeto. Por isso, chamou esse método 3 vezes.
fonte