Como um método sobrecarregado é escolhido quando um parâmetro é o valor nulo literal?

98

Me deparei com esta questão em um teste,

public class MoneyCalc {

   public void method(Object o) {
      System.out.println("Object Verion");
   }

   public void method(String s) {
      System.out.println("String Version");
   }

   public static void main(String args[]) {
      MoneyCalc question = new MoneyCalc();
      question.method(null);
   }
}

A saída deste programa é "String Version". Mas não consegui entender por que passar um nulo para um método sobrecarregado escolheu a versão de string. É null uma variável String apontando para nada?

No entanto, quando o código é alterado para,

public class MoneyCalc {

   public void method(StringBuffer sb) {
      System.out.println("StringBuffer Verion");
   }

   public void method(String s) {
      System.out.println("String Version");
   }

   public static void main(String args[]) {
      MoneyCalc question = new MoneyCalc();
      question.method(null);
   }
}

ele dá um erro de compilação dizendo "O método do método (StringBuffer) é ambíguo para o tipo MoneyCalc"

zakSyed
fonte
Você pode atribuir uma string a um valor nulo para que seja válido e a ordem para java e a maioria das linguagens de programação seja adequada para o tipo mais próximo e depois para o objeto.
JonH
6
Aparentemente, este foi fechado como duplicado por pessoas que apenas leram o título. A questão real aqui é sobre por que uma sobrecarga específica foi escolhida, não "o que é nulo".
intervalo de

Respostas:

102

É null uma variável String apontando para nada?

Uma referência nula pode ser convertida em uma expressão de qualquer tipo de classe. Então, no caso de String, isso é bom:

String x = null;

A Stringsobrecarga aqui é escolhida porque o compilador Java escolhe a sobrecarga mais específica , conforme a seção 15.12.2.5 do JLS . Em particular:

A intuição informal é que um método é mais específico do que outro se qualquer chamada tratada pelo primeiro método puder ser passada para o outro sem um erro de tipo em tempo de compilação.

Em seu segundo caso, os dois métodos ainda são aplicáveis, mas nenhum Stringnem StringBufferé mais específico do que o outro, portanto, nenhum método é mais específico do que o outro, daí o erro do compilador.

Jon Skeet
fonte
3
E como a sobrecarga de String é mais específica do que a sobrecarga de Object?
user1610015
12
Porque um Objectpode pegar qualquer tipo e envolvê-lo em um Object, enquanto um Stringsó pode pegar um String. Nesse caso, Stringé mais específico de um tipo em comparação com o Objecttipo.
JonH
10
@zakSyed Se lhe perguntassem o que é mais especializado "String" ou "Object", o que você diria? Evidentemente "String", certo? Se você foi perguntado: o que é mais especializado "String" ou "StringBuffer"? Não há resposta, ambas são especializações ortogonais, como você pode escolher entre elas? Então você deve ser explícito em relação a qual você quer (ou seja, lançando sua referência nula question.method((String)null))
Edwin Dalorzo
1
@JonSkeet: Isso faz sentido. Basicamente, ele procura um método de acordo com a regra mais específica e, se não for capaz de decidir qual é mais específico, ele gerará um erro em tempo de compilação.
zakSyed
3
@JonH "nulo" é o tipo de referência, se um dos métodos receber um tipo primitivo como parâmetro (ou seja, int), ele nem mesmo será considerado pelo compilador ao escolher o método correto a ser invocado para uma referência do tipo nulo. É confuso se por "Int" você quis dizer java.lang.Integer ou se você quis dizer o tipo primitivo int.
Edwin Dalorzo
9

Além disso, o JLS 3.10.7 também declara que "nulo" é um valor literal do "tipo nulo". Portanto, existe um tipo chamado "nulo".

Posteriormente, o JLS 4.1 afirma que existe um tipo nulo do qual é impossível declarar variáveis, mas você pode usá-lo apenas por meio do literal nulo. Mais tarde, diz:

A referência nula sempre pode sofrer uma conversão de referência de alargamento para qualquer tipo de referência.

Por que o compilador opta por ampliá-lo para String pode muito bem ser explicado na resposta de Jon .

Edwin Dalorzo
fonte
Tenho certeza de que esse tipo é vazio.
xavierm02
2
@ xavierm02 Não há menção disso na Especificação da linguagem Java. Você pode citar sua referência para que todos possamos verificar sua reivindicação?
Edwin Dalorzo
Eu nunca li isso. Mas eu fiz um pouco de Java neste verão e você pode sobrecarregar um método pegando um objeto com um método tomando um objeto Void.
xavierm02
É mais uma classe do que um tipo.
xavierm02
4
@ xavierm02 Claro que pode. Você pode sobrecarregar um método em Java com qualquer outro tipo que desejar. Isso, no entanto, não tem nada a ver com a pergunta ou minha resposta.
Edwin Dalorzo
2

Você pode atribuir a stringa um nullvalor para que seja válido e o pedido para java e a maioria das linguagens de programação seja adequado para o tipo mais próximo e depois para o objeto.

JonH
fonte
2

Para responder à pergunta no título: nullnão é nem a Stringnem an Object, mas uma referência a qualquer um pode ser atribuída null.

Na verdade, estou surpreso que esse código até compile. Tentei algo semelhante anteriormente e recebi um erro do compilador dizendo que a chamada era ambígua.

No entanto, neste caso, parece que o compilador está escolhendo o método mais baixo na cadeia alimentar. Presume-se que você queira a versão menos genérica do método para ajudá-lo.

Terei que ver se consigo descobrir o exemplo em que obtive um erro do compilador neste (aparentemente) mesmo cenário ...]

EDIT: entendo. Na versão que fiz, eu tinha dois métodos sobrecarregados aceitando um Stringe um Integer. Nesse cenário, não há parâmetro "mais específico" (como em Objecte String), portanto, ele não pode escolher entre eles, ao contrário do seu código.

Pergunta muito legal!

asteri
fonte
null não é nenhum, mas pode ser atribuído a ambos, essa é a diferença e ele irá compilar por que não - é um código absolutamente válido.
JonH
0

Como o tipo String é mais específico do que o tipo Object. Digamos que você adicione mais um método de tipo Integer.

public void method(Integer i) {
      System.out.println("Integer Version");
   }

Então, você obterá um erro do compilador dizendo que a chamada é ambígua. Como agora, temos dois métodos igualmente específicos com a mesma precedência.

BSingh
fonte
0

O compilador Java fornece a maioria dos tipos de classe derivados para atribuir nulos.

Aqui está o exemplo para entender:

class A{

    public void methodA(){
        System.out.println("Hello methodA");
    }
}

class B extends A{
    public void methodB(){
        System.out.println("Hello methodB");
    }
}

class C{
    public void methodC(){
        System.out.println("Hello methodC");
    }
}

public class MyTest {

     public static void fun(B Obj){
         System.out.println("B Class.");
     }
     public static void fun(A Obj){
         System.out.println("A Class.");
     }

    public static void main(String[] args) {
        fun(null);
    }
}

saída: Classe B.

por outro lado:

public class MyTest {

     public static void fun(C Obj){
         System.out.println("B Class.");
     }
     public static void fun(A Obj){
         System.out.println("A Class.");
     }

    public static void main(String[] args) {
        fun(null);
    }
}

Resultado: o método fun (C) é ambíguo para o tipo MyTest

Espero que ajude a entender melhor este caso.

Ravi
fonte
0

Fonte: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5
Conceito: Método mais específico
Explicação: Se mais de um método de membro estiver acessível e aplicável a uma chamada de método, é necessário escolher um para fornecer o descritor para o despacho do método em tempo de execução. A linguagem de programação Java usa a regra de que o método mais específico é escolhido. Tente lançar o nulo em um tipo específico e o método que você deseja será chamado automaticamente.

MP
fonte
No primeiro exemplo, String estende Object, então "mais específico" é o método de tomar String, mas no segundo exemplo, String e StringBuffer tanto estender objeto, para que eles sejam igualmente específicas e, portanto, o compilador não pode fazer uma escolha
David Kerr
-1

Eu diria que não. NULL é um estado, não um valor. Confira este link para obter mais informações sobre isso (o artigo se aplica ao SQL, mas acho que ajuda na sua pergunta também).

Polo Norte
fonte
nullé definitivamente um valor, conforme definido no JLS.
Sotirios Delimanolis