2016-03-21 6 views
15

Wenn Sie Varargs verwenden, wie wird der Komponententyp des resultierenden Arrays bestimmt?Wie wird der Komponententyp für das varargs-Array bestimmt?

Zum Beispiel ist dieses Programm garantiert zu drucken true oder ist sein Verhalten technisch nicht spezifiziert?

public static void main(String[] args) { 
    foo("", 0); 
} 

static <T> void foo(T... arr) { 
    System.out.println(arr.getClass() == Serializable[].class); 
} 
+2

Ich würde erwarten, dass die Antwort hier lautet "alle Regeln der Typinferenz in ihrer unergründlichen Komplexität, die immer zumindest einige Unterschiede zu jedem Compiler haben, den ich ausprobiert habe." –

+1

Wahrscheinlich: http://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html#jls-18.5.1 und folgende. – assylias

Antwort

1

Nun, ich bin nicht 100% sicher, aber ich denke, das nichts mit varargs zu tun hat, sondern eher wie ein dynamisches Bindung, Generika und Typ Löschung.

"Arrays in Java sind kovariant, aber Generics nicht. Mit anderen Worten, String [] ist ein Subtyp von Object [], aber Stack ist kein Subtyp von Stack."

Quelle: http://algs4.cs.princeton.edu/13stacks/

So Ihre Frage zu beantworten, dieses Verhalten dokumentiert angegeben wird und erwartet.

In dem Buch: 'Effective Java' Joshua Bloch erklärt es wie folgt aus:

Arrays von generischen Typen in zwei wichtigen Punkten unterscheiden. Erstens sind Arrays kovariant. Dieses unheimlich klingende Wort bedeutet einfach, dass, wenn Sub ein Untertyp von Super ist, der Array-Typ Sub [] ein Subtyp von Super [] ist. Generika dagegen sind invariant: für zwei verschiedene Typen Typ1 und Typ2 ist die Liste <Typ1> weder ein Subtyp noch ein Supertyp der Liste <Typ2> [JLS, 4.10; Naftalin07, 2.5]. Man könnte denken, dass dies bedeutet, dass Generika mangelhaft sind, aber wohl Arrays, die defizitär sind.

Und warum spreche ich über Arrays, die Sie fragen könnten? Nun, weil Varargs irgendwann zu Arrays werden.

"In früheren Versionen musste eine Methode, die eine beliebige Anzahl von Werten annahm, ein Array erstellen und die Werte vor dem Aufruf der Methode in das Array schreiben."

"Es ist immer noch wahr, dass mehrere Argumente in einem Array übergeben werden müssen, aber die Varargs-Funktion automatisiert und verbirgt den Prozess."

Quelle: https://docs.oracle.com/javase/8/docs/technotes/guides/language/varargs.html

2

Diese Antwort nicht zu 100% sein könnte die Antwort, die Sie suchen, aber vielleicht hilft es. Im Allgemeinen gibt arr.getClass() einige Array-Klassen zurück (meist Object[].class, könnte aber auch Integer[].class, Number[].class oder sogar Serializable[].class sein - normalerweise der spezifischste Typ aller Elemente, aber ich würde nicht darauf zählen, siehe m- szalik Antwort). Wenn Sie sicherstellen möchten, dass alle im Array enthaltenen Klassen eine Instanz von Serializable sind, müssten Sie jedes Element überprüfen (zw.die dargestellte Implementierung unterstützt nicht null Werte):

static <T> void foo(T... arr) { 
    System.out.println(Stream.of(arr) 
      .filter(e -> !Serializable.class.isInstance(e.getClass())) 
      .findFirst() 
      .orElse(null) == null); 
} 

Sie können einen Blick haben wollen:

Btw. Ich stimme mit der Meinung von Kapitän Fogetti:

Nun, ich bin nicht 100% sicher, aber ich denke, das nichts mit varargs zu tun hat, sondern eher wie ein dynamisches Bindung, Generika und Typ Löschung.

HINWEIS:

nur einige Beispiele für Ihre foo Umsetzung:

  • foo(1, 2) wäre Integer[].class
  • foo(1, 2.0)Number[].class
  • foo ("", 1) wäre wäre Serializable[].class
  • foo(null, 2) wäre Integer[].class
  • foo("", new Object())Object[].class
1

wäre Es geht nicht um varargs aber mehr über Generika.
Generische Informationen gehen während der Kompilierung verloren. Ihr varargs-Parameter wird vom Compiler in ein Array konvertiert. JVM entscheidet also nicht über den Typ, sondern über den Compiler.

Ihr Code wird an einen Bytecode als folows zusammengestellt:

public static void main(String[] args) { 
    foo(new Serializable[]{"", Integer.valueOf(0)}); 
} 

Der Algorithmus der Entscheidung ist ziemlich lang, aber man kann hier darüber lesen. https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html

3

lief ich diesen Code und die Ausgabe sagt Sie keine Garantien haben (zumindest, wenn Klassen auf verschiedenen Hierarchiezweigen mehr als einen gemeinsamen Vorfahren haben)

Ehrlich gesagt weiß ich nicht, die Gründe für diese Magie, aber ich konnte einfach nicht per Post als Kommentar

import java.util.*; 
import java.lang.*; 
import java.io.*; 

class Ideone 
{ 

    interface A{} 
    interface B{} 
    class AB implements A, B {} 
    class BA implements A, B {} 

    public static void main (String[] args) throws java.lang.Exception 
    { 
     foo(new AB(), new BA()); 
     foo2(new AB(), new BA()); 
    } 

    static <T> void foo(T... arr) { 
     System.out.println(arr.getClass() == A[].class); 
    } 

    static <T> void foo2(T... arr) { 
     System.out.println(arr.getClass() == B[].class); 
    } 
} 

Ausgang

true 
false 

Mehr seltsame Dinge:

Wenn interface B vor interface A erklärt wird, ist das Ergebnis das Gegenteil:

false 
true 

Ändern der Reihenfolge der Argumente in Methodenaufruf Methodendeklaration Ordnung und Reihenfolge der Schnittstellen in implements Block tut keine Wirkung für mich (1.8.0_51).

Verwandte Themen