Antworten:
Das Problem ist, dass Java bei Verwendung des Literal null nicht weiß, welcher Typ es sein soll. Es kann sich um ein Nullobjekt oder um ein Nullobjektarray handeln. Für ein einzelnes Argument wird letzteres angenommen.
Sie haben zwei Möglichkeiten. Wandeln Sie die Null explizit in Object um oder rufen Sie die Methode mit einer stark typisierten Variablen auf. Siehe das folgende Beispiel:
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));
}
}
Ausgabe:
foo called, args: [a, b, c]
foo called, args: [null, null]
foo called, args: [null]
foo called, args: [null]
asList()
ist ein statischer Import aus der java.util.Arrays
Klasse. Ich habe nur angenommen, dass es offensichtlich ist. Obwohl ich jetzt darüber nachdenke, hätte ich es wahrscheinlich nur verwenden sollen, Arrays.toString()
da der einzige Grund, warum es in eine Liste konvertiert wird, darin besteht, dass es hübsch gedruckt wird.
Sie benötigen eine explizite Besetzung für Object
:
foo((Object) null);
Andernfalls wird angenommen, dass das Argument das gesamte Array ist, das die Varargs darstellen.
Ein Testfall zur Veranschaulichung:
Der Java-Code mit einer vararg-take-Methodendeklaration (die zufällig statisch ist):
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;
}
}
Dieser Java-Code (ein JUnit4-Testfall) ruft das Obige auf (wir verwenden den Testfall, um nichts zu testen, nur um eine Ausgabe zu generieren):
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"}));
}
}
Wenn Sie dies als JUnit-Test ausführen, erhalten Sie:
sendNothing (): empfangenes 'x' ist ein Array der Größe 0 sendNullWithNoCast (): empfangenes 'x' ist null sendNullWithCastToString (): empfangenes 'x' ist ein Array der Größe 1 sendNullWithCastToArray (): empfangenes 'x' ist null sendOneValue (): empfangenes 'x' ist ein Array der Größe 1 sendThreeValues (): empfangenes 'x' ist ein Array der Größe 3 sendArray (): empfangenes 'x' ist ein Array der Größe 3
Um dies interessanter zu gestalten, rufen wir die receive()
Funktion aus Groovy 2.1.2 auf und sehen, was passiert. Es stellt sich heraus, dass die Ergebnisse nicht gleich sind! Dies kann jedoch ein Fehler sein.
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"
}
}
Wenn Sie dies als JUnit-Test ausführen, erhalten Sie Folgendes, wobei der Unterschied zu Java fett hervorgehoben ist.
sendNothing (): empfangenes 'x' ist ein Array der Größe 0 sendNullWithNoCast (): empfangenes 'x' ist null sendNullWithCastToString (): empfangenes 'x' ist null sendNullWithCastToArray (): empfangenes 'x' ist null sendOneValue (): empfangenes 'x' ist ein Array der Größe 1 sendThreeValues (): empfangenes 'x' ist ein Array der Größe 3 sendArray (): empfangenes 'x' ist ein Array der Größe 3
Dies liegt daran, dass eine varargs-Methode mit einem tatsächlichen Array anstelle einer Reihe von Array-Elementen aufgerufen werden kann. Wenn Sie es mit dem Mehrdeutigen null
an sich versehen, geht es davon aus, dass es sich null
um ein handelt Object[]
. Durch Casting des null
to Object
wird dies behoben.
ich bevorzuge
foo(new Object[0]);
um Nullzeigerausnahmen zu vermeiden.
Ich hoffe es hilft.
foo()
?
Die Reihenfolge für die Auflösung der Methodenüberladung lautet ( https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.12.2 ):
In der ersten Phase wird eine Überlastungsauflösung durchgeführt, ohne dass eine Boxing- oder Unboxing-Konvertierung oder die Verwendung eines Methodenaufrufs mit variabler Arität zulässig ist. Wenn während dieser Phase keine anwendbare Methode gefunden wird, wird die Verarbeitung zur zweiten Phase fortgesetzt.
Dies garantiert, dass alle Aufrufe, die in der Programmiersprache Java vor Java SE 5.0 gültig waren, aufgrund der Einführung von Methoden mit variabler Arität, implizitem Boxen und / oder Unboxing nicht als mehrdeutig angesehen werden. Die Deklaration einer Methode mit variabler Arität (§8.4.1) kann jedoch die für einen bestimmten Aufruf der Methodenmethode aufgerufene Methode ändern, da eine Methode mit variabler Arität in der ersten Phase als Methode mit fester Arität behandelt wird. Wenn Sie beispielsweise m (Objekt ...) in einer Klasse deklarieren, die bereits m (Objekt) deklariert, wird m (Objekt) für einige Aufrufausdrücke (wie m (null)) nicht mehr als m (Objekt [] ausgewählt. ) ist spezifischer.
Die zweite Phase führt eine Überlastungsauflösung durch, während das Ein- und Auspacken zugelassen wird, schließt jedoch die Verwendung des Aufrufs einer Methode mit variabler Arität aus. Wenn während dieser Phase keine anwendbare Methode gefunden wird, wird die Verarbeitung zur dritten Phase fortgesetzt.
Dies stellt sicher, dass eine Methode niemals durch Aufrufen einer Methode mit variabler Arität ausgewählt wird, wenn sie durch Aufrufen einer Methode mit fester Arität anwendbar ist.
In der dritten Phase kann Überladung mit variablen Aritätsmethoden, Boxen und Unboxen kombiniert werden.
foo(null)
stimmt foo(Object... arg)
mit arg = null
in der ersten Phase überein . arg[0] = null
wäre die dritte Phase, die nie passiert.
asList
Methode in der Stichprobe und ihren Zweck aufgeführt hätten.