Java SafeVarargs-Anmerkung, gibt es einen Standard oder eine bewährte Methode?

Lesezeit: 6 Minuten

Benutzer-Avatar
Oren

Ich bin vor kurzem auf Java gestoßen @SafeVarargs Anmerkung. Das Googeln nach dem, was eine variadische Funktion in Java unsicher macht, hat mich ziemlich verwirrt (Haufenvergiftung? gelöschte Typen?), also würde ich gerne ein paar Dinge wissen:

  1. Was macht eine variadische Java-Funktion im @SafeVarargs sinnvoll (am besten in Form eines vertiefenden Beispiels erläutert)?

  2. Warum bleibt diese Anmerkung dem Ermessen des Programmierers überlassen? Ist das nicht etwas, was der Compiler überprüfen können sollte?

  3. Gibt es einen Standard, an den man sich halten muss, um sicherzustellen, dass seine Funktion tatsächlich varagssicher ist? Wenn nicht, was sind die Best Practices, um dies sicherzustellen?

  • Haben Sie das Beispiel (und die Erklärung) in gesehen JavaDoc?

    – jlordo

    9. Januar 2013 um 8:32 Uhr


  • Für Ihre dritte Frage besteht eine Praxis darin, immer ein erstes Element und andere zu haben: retType myMethod(Arg first, Arg... others). Wenn Sie nicht angeben firstein leeres Array ist zulässig, und Sie haben möglicherweise eine Methode mit demselben Namen und demselben Rückgabetyp, die keine Argumente annimmt, was bedeutet, dass es für die JVM schwierig sein wird, festzustellen, welche Methode aufgerufen werden soll.

    – fg

    9. Januar 2013 um 8:42 Uhr


  • @jlordo habe ich getan, aber ich verstehe nicht, warum es im varargs-Kontext angegeben ist, da man diese Situation leicht außerhalb einer varargs-Funktion replizieren kann (bestätigt, kompiliert mit erwarteten Typsicherheitswarnungen und -fehlern zur Laufzeit).

    – Ören

    9. Januar 2013 um 8:51 Uhr


Benutzer-Avatar
neues Konto

1) Es gibt viele Beispiele im Internet und auf StackOverflow zu dem speziellen Problem mit Generics und Varargs. Grundsätzlich ist dies der Fall, wenn Sie eine variable Anzahl von Argumenten eines Typparametertyps haben:

<T> void foo(T... args);

In Java sind Varargs ein syntaktischer Zucker, der zur Kompilierzeit einfach “umgeschrieben” wird: ein Varargs-Parameter vom Typ X... wird in einen Parameter vom Typ konvertiert X[]; und jedes Mal, wenn diese varargs-Methode aufgerufen wird, sammelt der Compiler alle “variablen Argumente”, die in den varargs-Parameter eingehen, und erstellt genau wie ein Array new X[] { ...(arguments go here)... }.

Dies funktioniert gut, wenn der Typ varargs konkret ist String.... Wenn es sich um eine Typvariable wie z T...es funktioniert auch wenn T ist bekanntermaßen ein konkreter Typ für diesen Anruf. zB wenn die obige Methode Teil einer Klasse wäre Foo<T>und du hast eine Foo<String> Referenz, dann anrufen foo auf es wäre okay, weil wir wissen T ist String an dieser Stelle im Code.

Es funktioniert jedoch nicht, wenn der “Wert” von T ist ein weiterer Typparameter. In Java ist es unmöglich, ein Array eines Typparameter-Komponententyps (new T[] { ... }). Also verwendet Java stattdessen new Object[] { ... } (hier Object ist die Obergrenze von T; Wenn die Obergrenze etwas anderes wäre, wäre es das anstelle von Object) und gibt Ihnen dann eine Compilerwarnung aus.

Also, was ist falsch am Erstellen new Object[] Anstatt von new T[] oder Wasauchimmer? Nun, Arrays in Java kennen ihren Komponententyp zur Laufzeit. Somit hat das übergebene Array-Objekt zur Laufzeit den falschen Komponententyp.

Für die wahrscheinlich häufigste Verwendung von varargs, einfach über die Elemente zu iterieren, ist dies kein Problem (Sie interessieren sich nicht für den Laufzeittyp des Arrays), also ist dies sicher:

@SafeVarargs
final <T> void foo(T... args) {
    for (T x : args) {
        // do stuff with x
    }
}

Für alles, was vom Laufzeitkomponententyp des übergebenen Arrays abhängt, ist es jedoch nicht sicher. Hier ist ein einfaches Beispiel für etwas, das unsicher ist und abstürzt:

class UnSafeVarargs
{
  static <T> T[] asArray(T... args) {
    return args;
  }

  static <T> T[] arrayOfTwo(T a, T b) {
    return asArray(a, b);
  }

  public static void main(String[] args) {
    String[] bar = arrayOfTwo("hi", "mom");
  }
}

Das Problem dabei ist, dass wir von der Art abhängig sind args sein T[] um es als zurückzugeben T[]. Aber eigentlich ist der Typ des Arguments zur Laufzeit keine Instanz von T[].

3) Wenn Ihre Methode ein Argument vom Typ hat T... (wobei T ein beliebiger Typparameter ist), dann:

  • Sicher: Wenn Ihre Methode nur davon abhängt, dass die Elemente des Arrays Instanzen von sind T
  • Unsicher: Wenn es darauf ankommt, dass das Array eine Instanz von ist T[]

Zu den Dingen, die vom Laufzeittyp des Arrays abhängen, gehören: Rückgabe als Typ T[]indem Sie es als Argument an einen Parameter vom Typ übergeben T[]um den Array-Typ mit abzurufen .getClass()und übergibt es an Methoden, die vom Laufzeittyp des Arrays abhängen, wie z List.toArray() und Arrays.copyOf()etc.

2) Die oben erwähnte Unterscheidung ist zu kompliziert, um einfach automatisch unterschieden zu werden.

  • naja jetzt ergibt alles sinn. Danke. Also nur um zu sehen, dass ich Sie vollständig verstehe, nehmen wir an, wir haben eine Klasse FOO<T extends Something> Wäre es sicher, das T zurückzugeben?[] einer varargs-Methode als Array von Somthings ?

    – Ören

    10. Januar 2013 um 8:11 Uhr

  • Es könnte erwähnenswert sein, dass es einen Fall gibt, in dem es (vermutlich) immer richtig ist, es zu verwenden @SafeVarargs ist, wenn Sie das Array nur an eine andere Methode übergeben, die bereits so annotiert ist (zum Beispiel: Ich schreibe häufig vararg-Methoden, die existieren, um ihr Argument in eine Liste umzuwandeln Arrays.asList(...) und an eine andere Methode übergeben; ein solcher Fall kann immer mit kommentiert werden @SafeVarargs Weil Arrays.asList hat die Anmerkung).

    – Jules

    12. April 2014 um 12:44 Uhr


  • @newacct Ich weiß nicht, ob dies in Java 7 der Fall war, aber Java 8 erlaubt dies nicht @SafeVarargs es sei denn, die Methode ist static oder finalda es sonst in einer abgeleiteten Klasse mit etwas Unsicherem überschrieben werden könnte.

    – Andy

    28. April 2017 um 15:48 Uhr


  • und in Java 9 wird es auch erlaubt sein private Methoden, die ebenfalls nicht überschrieben werden können.

    – David L.

    4. Mai 2017 um 16:50 Uhr

Berücksichtigen Sie dies für Best Practices.

Wenn Sie dies haben:

public <T> void doSomething(A a, B b, T... manyTs) {
    // Your code here
}

Ändern Sie es so:

public <T> void doSomething(A a, B b, T... manyTs) {
    doSomething(a, b, Arrays.asList(manyTs));
}

private <T> void doSomething(A a, B b, List<T> manyTs) {
    // Your code here
}

Ich habe festgestellt, dass ich normalerweise nur Varargs hinzufüge, um es für meine Anrufer bequemer zu machen. Für meine interne Implementierung wäre es fast immer bequemer, a zu verwenden List<>. Also zum Huckepack Arrays.asList() und sicherstellen, dass ich auf keinen Fall Heap Pollution einführen kann, das ist, was ich tue.

Ich weiß, das beantwortet nur deine #3. newacct hat eine großartige Antwort für Nr. 1 und Nr. 2 oben gegeben, und ich habe nicht genug Ruf, um dies einfach als Kommentar zu hinterlassen. 😛

1353280cookie-checkJava SafeVarargs-Anmerkung, gibt es einen Standard oder eine bewährte Methode?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy