Pessoal estou com uma duvida cruel, agora, estudando para o SCJP.
Olha só:
Se
é falso
Por que isto funciona:
public class Duvida1{
public void qualMetodoChama(String s){
System.out.println("Metodo String");
}
public void qualMetodoChama(Object o){
System.out.println("Metodo Object");
}
public static void main(String[] args){
Duvida1 d = new Duvida1();
d.qualMetodoChama(null);
}
}
O null é um valor especial, que pode ser atribuído a qualquer classe de objetos. Ele indica a ausência de uma referência para um objeto, o que significa que ele em si seja um objeto.
No caso da sua chamada, houve ambiguidade e o compilador teve de escolher entre um dos dois métodos. Ele optou pelo mais específico. Se você trocar o método String por um método Integer, ele também irá chamar o método Integer. Isso não tem muita relação com o tipo da variável null.
Note que você também poderia “fazer um cast” do null, para que o método correto fosse chamado. Ou seja, se o null estivesse dentro de uma variável, esse problema não ocorreria.
[quote]
Note que você também poderia “fazer um cast” do null, para que o método correto fosse chamado. Ou seja, se o null estivesse dentro de uma variável, esse problema não ocorreria.[/quote]
Realmente se colocar um cast no null dá para escolher o metodo, isto faz sentido.
Ok.
Más analisando um pouco mais constatei outra coisa.
Se eu sobrecarregar outro metodo com qualquer outro argumento sem ser do tipo “Object”, como no meu exemplo Integer.
public class Duvida1{
public void qualMetodoChama(Integer i){
System.out.println(“Metodo1: Null chama Integer”);
}
public void qualMetodoChama(String s){
System.out.println("Metodo String");
}
public void qualMetodoChama(Object o){
System.out.println("Metodo Object");
}
public static void main(String[] args){
Duvida1 d = new Duvida1();
d.qualMetodoChama(null);
}
}
Dai sim a JVM vai ver a ambiguidade, reclamará com um erro de compilação.
Editado
Ops, eu quis dizer “compilador” e nao JVM… heheheh
[quote=gulira]
Más analisando um pouco mais constatei outra coisa.
Se eu sobrecarregar outro metodo com qualquer outro argumento sem ser do tipo “Object”, como no meu exemplo Integer.
Dai sim a JVM vai ver a ambiguidade, reclamará com um erro de compilação.[/quote]
[EDIT]
Na verdade, o Java tem uma característica que na minha opinião está mais para um comportamento problemático…
Se você tem dois métodos sobrecarregados, um deles aceitando um tipo, e outro uma classe pai desse tipo, a VM não chamará o método mais específico, caso a referência variável seja do tipo mais genérico. Ou seja, o conteúdo pouco importa, já que qual método será chamado é definido de maneira estática, pelo compilador.
Então, nesse seu exemplo, mesmo que houvesse uma variável Object, com um valor String, o Java chamaria o método que aceita o tipo Object, por se tratar de uma superclasse de String e por ter um método sobrecarregado para ele.
Esse é um dos cuidados ressaltados no livro Effective Java.
[quote=ViniGodoy][quote=gulira]
Más analisando um pouco mais constatei outra coisa.
Se eu sobrecarregar outro metodo com qualquer outro argumento sem ser do tipo “Object”, como no meu exemplo Integer.
Dai sim a JVM vai ver a ambiguidade, reclamará com um erro de compilação.[/quote]
Na verdade, o Java tem uma característica que na minha opinião está mais para um comportamento problemático…
Se você tem dois métodos sobrecarregados, um deles aceitando um tipo, e outro uma classe pai desse tipo, a VM não chamará o método mais específico, necessariamente. Ela é livre para escolher o que quiser.
Então, nesse seu exemplo, mesmo que houvesse uma variável String, com um valor String, haveria o risco do Java chamar o método que aceita o tipo Object, por se tratar de uma superclasse de String. Qual método será chamado pode variar, até mesmo entre duas execuções de um mesmo programa.
Esse é um dos cuidados ressaltados no livro Effective Java.[/quote]
Opa. Tem certeza? Na verdade quem “faz a escolha” é o compilador. No código compilado não há ambigüidade já que cada método invocado é definido com a signature completa (incluindo os parâmetros) do método. Então não vejo como esse problema seria possível. Será que tem mais algum elemento envolvido nessa situação problematica? Reflection?
EDIT: Se eu tivesse o livro, não estaria fazendo essas perguntas bestas… acho que preciso comprar mesmo.
O compilador não pode fazer a escolha pq de antemão ele não faz checagem de tipos. Por isso que uma das caracteristicas de java é late binding. Então somente em tempo de execução é determinado qual metodo será chamado para determinado objeto. Se fosse o compilador que escolhesse os metodos seria dificil redefinir metodos em subclasses, pq o compilador teria que determinar em tempo de compilação qual classe pertence o objeto.
O que você diz é válido para métodos sobre-escritos. Num caso desses se o objeto em questão tiver o método redefinido, esse método redefinido é invocado, claro.
Mas aqui, pelo que entendi, a questão era sobre métodos sobrecarregados. Para esses não há nada dinâmico.
Se eu tiver esse código:
public class Compiler {
public static void main(String[] args) {
new Compiler().test(null);
}
private void test(String str){
System.out.println("String");
}
public void test(Object str) {
System.out.println("Object");
}
}
Compilado, vai produzir isto:
[code]
Compiled from “Compiler.java"
public class Compiler extends java.lang.Object{
public Compiler();
Code:
0: aload_0
1: invokespecial #8; //Method java/lang/Object.”<init>":()V
4: return
que quer dizer: chame o método cujo nome é “test”, o tipo de retorno é void (“V”) e o parâmetro é do tipo String (“Ljava/lang/String;”). Ou seja, o compilador já fez a escolha de chamar o método cujo parâmetro é um String.
O importante é entender que a seleção entre métodos sobrecarregados é estática, enquanto a seleção entre métodos sobrescritos é dinâmica. A versão correta de um método sobrescrito é escolha em tempo de execução, baseado no tipo Runtime do objeto no qual o método é chamado.
A versão que o compilador irá chamar, em tempo de execução, depende mais do tipo da variável no momento da compilação, e não do tipo de dados do seu conteúdo. O código abaixo demonstra o fenômeno (retirado do item 26, Core Java, Use overloading judiciously)
public class CollectionClassifier {
public static String classify(Set s) {
return "Set";
}
public static String classify(List l) {
return "List";
}
public static String classify(Collection c) {
return "Collection";
}
public static void main(String[] args) {
Collection[] testes = new Collection[] {
new HashSet(),
new ArrayList(),
new HashMap.values() //Neither Set nor List
};
for (int i = 0; i < testes.length; i++)
System.out.println(classify(tests[i]));
}
}
Quem esperar que esse programa retorne Set, List e Unknown Collection vai ter uma desagradável surpresa.
O java sempre irá utilizar o tipo Collection, pois é o que pode ser definido em tempo de compilação.
O problema de abordagens desse tipo é que, diferentes métodos podem conter diferentes referências e, por esse detalhe, você observa o programa (ou o depurador) se comportando de maneira aparentemente diferente durante a execução.
Para quem tiver mais interesse de ver o texto original, esse capítulo está como exemplo, no site oficial do livro e se trata do item 26.
[quote=Sami Koivu]
Opa. Tem certeza? Na verdade quem “faz a escolha” é o compilador.[/quote]
Tem razão, depois que você falou não tive tanta certeza e resolvi conferir as fontes. Já corrigi também o post original, assim quem achar o tópico não corre o risco de ler até a metade e ficar com uma explicação incorreta.
[quote=ViniGodoy]O importante é entender que a seleção entre métodos sobrecarregados é estática, enquanto a seleção entre métodos sobrescritos é dinâmica.
[/quote]
Acho que a única informação que faltou mostrar aqui é que qualquer operação de instanceof onde o argumento da esquerda seja null irá retornar false. Ou seja, null instanceof String retorna false, null instanceof Object também.