Risposte:
Il problema è che quando usi il valore letterale null, Java non sa che tipo dovrebbe essere. Potrebbe essere un oggetto null o un array di oggetti null. Per un singolo argomento assume quest'ultimo.
Hai due scelte. Esegui il cast del null in modo esplicito su Object o chiama il metodo utilizzando una variabile fortemente tipizzata. Guarda l'esempio di seguito:
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));
}
}
Produzione:
foo called, args: [a, b, c]
foo called, args: [null, null]
foo called, args: [null]
foo called, args: [null]
asList()
è un'importazione statica dalla java.util.Arrays
classe. Ho solo pensato che fosse ovvio. Anche se ora che ci sto pensando, probabilmente avrei dovuto usarlo Arrays.toString()
poiché l'unico motivo per cui viene convertito in un elenco è che verrà stampato abbastanza.
Hai bisogno di un cast esplicito per Object
:
foo((Object) null);
Altrimenti si presume che l'argomento sia l'intero array rappresentato dai vararg.
Un caso di prova per illustrare questo:
Il codice Java con una dichiarazione del metodo che accetta vararg (che sembra essere statica):
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;
}
}
Questo codice Java (un test case JUnit4) chiama quanto sopra (stiamo usando il test case non per testare nulla, solo per generare un output):
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"}));
}
}
Eseguendolo come un test JUnit si ottiene:
sendNothing (): ha ricevuto 'x' è un array di dimensione 0 sendNullWithNoCast (): la "x" ricevuta è nullo sendNullWithCastToString (): ha ricevuto 'x' è un array di dimensione 1 sendNullWithCastToArray (): la "x" ricevuta è nullo sendOneValue (): ricevuto 'x' è un array di dimensione 1 sendThreeValues (): ricevuto 'x' è un array di dimensione 3 sendArray (): ricevuto 'x' è un array di dimensione 3
Per renderlo più interessante, chiamiamo la receive()
funzione da Groovy 2.1.2 e vediamo cosa succede. Si scopre che i risultati non sono gli stessi! Questo potrebbe essere un bug però.
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"
}
}
Eseguendolo come un test JUnit si ottiene quanto segue, con la differenza con Java evidenziata in grassetto.
sendNothing (): ha ricevuto 'x' è un array di dimensione 0 sendNullWithNoCast (): la "x" ricevuta è nullo sendNullWithCastToString (): la "x" ricevuta è nullo sendNullWithCastToArray (): la "x" ricevuta è nullo sendOneValue (): ricevuto 'x' è un array di dimensione 1 sendThreeValues (): ricevuto 'x' è un array di dimensione 3 sendArray (): ricevuto 'x' è un array di dimensione 3
preferisco
foo(new Object[0]);
per evitare eccezioni relative al puntatore null.
Spero che sia d'aiuto.
foo()
?
L'ordine per la risoluzione del sovraccarico del metodo è ( https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.12.2 ):
La prima fase esegue la risoluzione del sovraccarico senza consentire la conversione di boxing o unboxing o l'uso di invocazione del metodo arity variabile. Se durante questa fase non viene trovato alcun metodo applicabile, l'elaborazione continua alla seconda fase.
Ciò garantisce che tutte le chiamate che erano valide nel linguaggio di programmazione Java prima di Java SE 5.0 non siano considerate ambigue come risultato dell'introduzione di metodi arity variabili, boxing implicito e / o unboxing. Tuttavia, la dichiarazione di un metodo arity variabile (§8.4.1) può cambiare il metodo scelto per un'espressione di invocazione di un metodo dato, poiché un metodo arity variabile è trattato come un metodo arity fisso nella prima fase. Ad esempio, la dichiarazione di m (Object ...) in una classe che dichiara già m (Object) fa sì che m (Object) non venga più scelto per alcune espressioni di invocazione (come m (null)), come m (Object [] ) è più specifico.
La seconda fase esegue la risoluzione del sovraccarico consentendo il boxing e unboxing, ma preclude comunque l'uso dell'invocazione del metodo arity variabile. Se durante questa fase non viene trovato alcun metodo applicabile, l'elaborazione prosegue fino alla terza fase.
Ciò garantisce che un metodo non venga mai scelto tramite il richiamo del metodo arity variabile se è applicabile tramite il richiamo del metodo arity fisso.
La terza fase consente di combinare il sovraccarico con metodi variabili di arità, boxe e unboxing.
foo(null)
partite foo(Object... arg)
con arg = null
nella prima fase. arg[0] = null
sarebbe la terza fase, che non accade mai.
asList
metodo nel campione e il suo scopo.