¿Cómo se elige un método sobrecargado cuando un parámetro es el valor literal nulo?

98

Me encontré con esta pregunta en un cuestionario,

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);
   }
}

La salida de este programa es "Versión de cadena". Pero no pude entender por qué pasar un nulo a un método sobrecargado eligió la versión de cadena. ¿Es null una variable de cadena que apunta a nada?

Sin embargo, cuando se cambia el código a,

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);
   }
}

da un error de compilación que dice "El método método (StringBuffer) es ambiguo para el tipo MoneyCalc"

zakSyed
fuente
Puede asignar una cadena a un valor nulo para que sea válido y el orden para java y la mayoría de los lenguajes de programación se ajuste al tipo más cercano y luego al objeto.
JonH
6
Aparentemente esto fue cerrado como duplicado por personas que solo leyeron el título. La pregunta real aquí es por qué se eligió una sobrecarga específica, no "qué es nulo".
interjay

Respuestas:

102

¿Es null una variable de cadena que apunta a nada?

Una referencia nula se puede convertir en una expresión de cualquier tipo de clase. Entonces, en el caso de String, esto está bien:

String x = null;

La Stringsobrecarga aquí se elige porque el compilador de Java elige la sobrecarga más específica , según la sección 15.12.2.5 de JLS . En particular:

La intuición informal es que un método es más específico que otro si cualquier invocación manejada por el primer método podría pasarse al otro sin un error de tipo en tiempo de compilación.

En su segundo caso, ambos métodos son todavía aplicables, pero tampoco Stringni StringBufferes más específico que el otro, por lo tanto, ninguno de los métodos es más específico que el otro, por lo tanto el error, compilador.

Jon Skeet
fuente
3
¿Y cómo es la sobrecarga de cadenas más específica que la sobrecarga de objetos?
user1610015
12
Porque an Objectpuede tomar cualquier tipo y envolverlo en un Object, mientras que a Stringsolo puede tomar un String. En este caso, Stringes más específico de un tipo en comparación con el Objecttipo.
JonH
10
@zakSyed Si te preguntaran qué es una "Cadena" u "Objeto" más especializado, ¿qué dirías? Evidentemente "String", ¿verdad? Si se le preguntó: ¿qué es más especializado "String" o "StringBuffer"? No hay respuesta, ambas son especializaciones ortogonales, ¿cómo se puede elegir entre ellas? Entonces debe ser explícito con respecto a cuál desea (es decir, emitiendo su referencia nula question.method((String)null))
Edwin Dalorzo
1
@JonSkeet: Eso tiene sentido. Entonces, básicamente, busca un método de acuerdo con la regla más específica y, si no puede decidir cuál es más específico, arrojará un error en tiempo de compilación.
zakSyed
3
@JonH "null" es un tipo de referencia, si uno de los métodos recibe un tipo primitivo como parámetro (es decir, int) ni siquiera será considerado por el compilador al elegir el método correcto para invocar una referencia de tipo null. Es confuso si por "Int" te refieres a java.lang.Integer o si te refieres al tipo primitivo int.
Edwin Dalorzo
9

Además, JLS 3.10.7 también declara que "nulo" es un valor literal del "tipo nulo". Por tanto, existe un tipo llamado "nulo".

Más tarde, el JLS 4.1 establece que existe un tipo nulo del cual es imposible declarar variables, pero puede usarlo solo a través del literal nulo. Luego dice:

La referencia nula siempre puede sufrir una conversión de referencia amplia a cualquier tipo de referencia.

Por qué el compilador elige ampliarlo a String bien podría explicarse en la respuesta de Jon .

Edwin Dalorzo
fuente
Estoy bastante seguro de que ese tipo es Void.
xavierm02
2
@ xavierm02 No se menciona eso en la Especificación del lenguaje Java. ¿Puede citar su referencia para que todos podamos verificar su reclamo?
Edwin Dalorzo
Nunca leí esa cosa. Pero hice algo de Java este verano y puedes sobrecargar un método que toma un Objeto con un método que toma un objeto Void.
xavierm02
Es más una clase que un tipo.
xavierm02
4
@ xavierm02 Por supuesto que puedes. Puede sobrecargar un método en Java con cualquier otro tipo que desee. Eso, sin embargo, no tiene nada que ver con la pregunta o mi respuesta.
Edwin Dalorzo
2

Puede asignar un stringa un nullvalor para que sea válido y el orden de java y la mayoría de los lenguajes de programación se ajuste al tipo más cercano y luego al objeto.

JonH
fuente
2

Para responder a la pregunta del título: nullno es ni un Stringni un Object, pero se puede asignar una referencia a cualquiera de los dos null.

De hecho, me sorprende que este código incluso se compile. Intenté algo similar anteriormente y obtuve un error del compilador que decía que la llamada era ambigua.

Sin embargo, en este caso, parece que el compilador está eligiendo el método más bajo en la cadena alimentaria. Se supone que desea la versión menos genérica del método para poder ayudarlo.

Tendré que ver si puedo desenterrar el ejemplo en el que obtuve un error del compilador en este (aparentemente) exactamente el mismo escenario, aunque ...]

EDITAR: Ya veo. En la versión que hice, tenía dos métodos sobrecargados que aceptaban un Stringy un Integer. En este escenario, no hay un parámetro "más específico" (como en Objecty String), por lo que no puede elegir entre ellos, a diferencia de su código.

¡Muy buena pregunta!

asteri
fuente
null no es ninguno, pero se puede asignar a ambos, esa es la diferencia y compilará por qué no lo haría: es un código absolutamente válido.
JonH
0

Dado que el tipo de cadena es más específico que el tipo de objeto. Digamos que agrega un método más que toma un tipo Integer.

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

Entonces obtendrá un error del compilador que dice que la llamada es ambigua. Como ahora tenemos dos métodos igualmente específicos con la misma precedencia.

BSingh
fuente
0

El compilador Java proporciona la mayoría de tipos de clases derivadas para asignar nulos.

Aquí está el ejemplo para entenderlo:

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);
    }
}

salida: Clase B.

por otra parte:

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: el método fun (C) es ambiguo para el tipo MyTest

Espero que ayude a comprender mejor este caso.

Ravi
fuente
0

Fuente: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5
Concepto: método más específico
Explicación: si más de un método miembro es accesible y aplicable a la invocación de un método, es necesario elegir uno para proporcionar el descriptor para el envío del método en tiempo de ejecución. El lenguaje de programación Java utiliza la regla de que se elige el método más específico. Intente convertir el nulo en un tipo específico y el método que desee se llamará automáticamente.

MP
fuente
En el primer ejemplo, cuerdas se extiende de objetos, por lo que "más específica" es el método de tomar cuerdas, pero en el segundo ejemplo, cuerdas y StringBuffer tanto objeto extendido, por lo que son igualmente específicas y por lo tanto el compilador no puede tomar una decisión
David Kerr
-1

Yo diría que no. NULL es un estado, no un valor. Consulte este enlace para obtener más información sobre esto (el artículo se aplica a SQL, pero creo que también ayuda con su pregunta).

Polo Norte
fuente
nulles definitivamente un valor, como se define en la JLS.
Sotirios Delimanolis