¿Llamar al método varargs de Java con un solo argumento nulo?

Si tengo un método vararg Java foo(Object ...arg) y llamo foo(null, null) , tengo ambos arg[0] y arg[1] como null s. Pero si llamo a foo(null) , arg sí es nulo. ¿Por qué está pasando esto?

¿Cómo debo llamar a foo para que foo.length == 1 && foo[0] == null sea true ?

El problema es que cuando usas el literal nulo, Java no sabe qué tipo se supone que es. Podría ser un Objeto nulo, o podría ser una matriz de Objeto nulo. Para un argumento único, asume lo segundo.

Tienes dos opciones. Emitir el nulo explícitamente a Object o llamar al método usando una variable fuertemente tipada. Vea el ejemplo a continuación:

 public class Temp{ public static void main(String[] args){ foo("a", "b", "c"); foo(null, null); foo((Object)null); Object bar = null; foo(bar); } private static void foo(Object...args) { System.out.println("foo called, args: " + asList(args)); } } 

Salida:

 foo called, args: [a, b, c] foo called, args: [null, null] foo called, args: [null] foo called, args: [null] 

Necesitas un elenco explícito para Object :

 foo((Object) null); 

De lo contrario, se supone que el argumento es la matriz completa que representan los varargs.

Un caso de prueba para ilustrar esto:

El código de Java con una statement de método vararg-taking (que pasa a ser estático):

 public class JavaReceiver { public static String receive(String... x) { String res = ((x == null) ? "null" : ("an array of size " + x.length)); return "received 'x' is " + res; } } 

Este código Java (un caso de prueba JUnit4) llama a lo anterior (estamos utilizando el caso de prueba para no probar nada, solo para generar algún resultado):

 import org.junit.Test; public class JavaSender { @Test public void sendNothing() { System.out.println("sendNothing(): " + JavaReceiver.receive()); } @Test public void sendNullWithNoCast() { System.out.println("sendNullWithNoCast(): " + JavaReceiver.receive(null)); } @Test public void sendNullWithCastToString() { System.out.println("sendNullWithCastToString(): " + JavaReceiver.receive((String)null)); } @Test public void sendNullWithCastToArray() { System.out.println("sendNullWithCastToArray(): " + JavaReceiver.receive((String[])null)); } @Test public void sendOneValue() { System.out.println("sendOneValue(): " + JavaReceiver.receive("a")); } @Test public void sendThreeValues() { System.out.println("sendThreeValues(): " + JavaReceiver.receive("a", "b", "c")); } @Test public void sendArray() { System.out.println("sendArray(): " + JavaReceiver.receive(new String[]{"a", "b", "c"})); } } 

Ejecutando esto como una prueba JUnit produce:

 sendNothing (): recibido 'x' es una matriz de tamaño 0
 sendNullWithNoCast (): recibido 'x' es nulo
 sendNullWithCastToString (): recibido 'x' es una matriz de tamaño 1
 sendNullWithCastToArray (): recibido 'x' es nulo
 sendOneValue (): recibido 'x' es una matriz de tamaño 1
 sendThreeValues ​​(): received 'x' es una matriz de tamaño 3
 sendArray (): recibido 'x' es una matriz de tamaño 3

Para que esto sea más interesante, llamemos a la función receive() de Groovy 2.1.2 y veamos qué sucede. ¡Resulta que los resultados no son los mismos! Aunque esto puede ser un error.

 import org.junit.Test class GroovySender { @Test void sendNothing() { System.out << "sendNothing(): " << JavaReceiver.receive() << "\n" } @Test void sendNullWithNoCast() { System.out << "sendNullWithNoCast(): " << JavaReceiver.receive(null) << "\n" } @Test void sendNullWithCastToString() { System.out << "sendNullWithCastToString(): " << JavaReceiver.receive((String)null) << "\n" } @Test void sendNullWithCastToArray() { System.out << "sendNullWithCastToArray(): " << JavaReceiver.receive((String[])null) << "\n" } @Test void sendOneValue() { System.out << "sendOneValue(): " + JavaReceiver.receive("a") << "\n" } @Test void sendThreeValues() { System.out << "sendThreeValues(): " + JavaReceiver.receive("a", "b", "c") << "\n" } @Test void sendArray() { System.out << "sendArray(): " + JavaReceiver.receive( ["a", "b", "c"] as String[] ) << "\n" } } 

Ejecutar esto como una prueba JUnit produce lo siguiente, con la diferencia de que Java se resalta en negrita.

 sendNothing (): recibido 'x' es una matriz de tamaño 0
 sendNullWithNoCast (): recibido 'x' es nulo
 sendNullWithCastToString (): recibido 'x' es nulo
 sendNullWithCastToArray (): recibido 'x' es nulo
 sendOneValue (): recibido 'x' es una matriz de tamaño 1
 sendThreeValues ​​(): received 'x' es una matriz de tamaño 3
 sendArray (): recibido 'x' es una matriz de tamaño 3

Esto se debe a que se puede invocar un método varargs con una matriz real en lugar de una serie de elementos de matriz. Cuando le proporciona el null ambiguo por sí mismo, asume que el null es un Object[] . Lanzar el null a Object arreglará esto.

yo prefiero

 foo(new Object[0]); 

para evitar excepciones del puntero nulo.

Espero eso ayude.