null - argumentos - ¿Llamar al método varargs de Java con un solo argumento nulo?
vararg kotlin (5)
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]
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.
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 declaración 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(): received ''x'' is an array of size 0 sendNullWithNoCast(): received ''x'' is null sendNullWithCastToString(): received ''x'' is an array of size 1 sendNullWithCastToArray(): received ''x'' is null sendOneValue(): received ''x'' is an array of size 1 sendThreeValues(): received ''x'' is an array of size 3 sendArray(): received ''x'' is an array of size 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(): received ''x'' is an array of size 0 sendNullWithNoCast(): received ''x'' is null sendNullWithCastToString(): received ''x'' is null sendNullWithCastToArray(): received ''x'' is null sendOneValue(): received ''x'' is an array of size 1 sendThreeValues(): received ''x'' is an array of size 3 sendArray(): received ''x'' is an array of size 3
yo prefiero
foo(new Object[0]);
para evitar excepciones del puntero nulo.
Espero eso ayude.