Java-varargs-Methode mit einem einzelnen Null-Argument aufrufen?


97

Wenn ich eine vararg Java-Methode habe foo(Object ...arg)und aufrufe foo(null, null), habe ich beide arg[0]und arg[1]als nulls. Aber wenn ich anrufe foo(null), ist argselbst null. Warum passiert dies?

Wie soll ich anrufen , fooso dass foo.length == 1 && foo[0] == nullist true?

Antworten:


95

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]

Wäre für andere hilfreich, wenn Sie die asListMethode in der Stichprobe und ihren Zweck aufgeführt hätten.
Arun Kumar

5
@ArunKumar asList()ist ein statischer Import aus der java.util.ArraysKlasse. 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.
Mike Deck

23

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.


Nicht wirklich, siehe meinen Beitrag.
Buhake Sindi

Ups, das gleiche hier ... Entschuldigung, Mitternachtsöl.
Buhake Sindi

6

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 berücksichtigt auch das Aufrufen der variadischen Funktion ohne Parameter
bebbo

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 nullan sich versehen, geht es davon aus, dass es sich nullum ein handelt Object[]. Durch Casting des nullto Objectwird dies behoben.


1

ich bevorzuge

foo(new Object[0]);

um Nullzeigerausnahmen zu vermeiden.

Ich hoffe es hilft.


14
Nun, wenn es eine Vararg-Methode ist, warum nicht einfach so nennen foo()?
BrainStorm.exe

@ BrainStorm.exe Ihre Antwort sollte als Antwort markiert sein. Vielen Dank.
MDP

1

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 ):

  1. 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.

  2. 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.

  3. In der dritten Phase kann Überladung mit variablen Aritätsmethoden, Boxen und Unboxen kombiniert werden.

foo(null)stimmt foo(Object... arg)mit arg = nullin der ersten Phase überein . arg[0] = nullwäre die dritte Phase, die nie passiert.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.