2016-03-01 3 views
23

Warum hat Collections.sort(List<T>) die Signatur:Collections.sort() Erklärung: Warum <? super T> statt <T>

public static <T extends Comparable<? super T>> void sort(List<T> list) 

und nicht:

public static <T extends Comparable<T>> void sort(List<? extends T> list) 
  • Ich verstehe, dass sie beide die gleiche dienen würde Zweck; Warum haben die Framework-Entwickler die erste Option verwendet?
  • Oder sind diese Deklarationen wirklich anders?

Antwort

19

Ihre vorgeschlagene Signatur würde wahrscheinlich in Java-8 funktionieren. In früheren Java-Versionen war die Inferenz jedoch nicht so intelligent. Bedenken Sie, dass Sie List<java.sql.Date> haben. Beachten Sie, dass java.sql.Date erweitert java.util.Date, die Comparable<java.util.Date> implementiert. Wenn Sie kompilieren

List<java.sql.Date> list = new ArrayList<>(); 
Collections.sort(list); 

Es funktioniert perfekt in Java-7. Hier wird T als java.sql.Date gewertet, was tatsächlich Comparable<java.util.Date> ist, was Comparable<? super java.sql.Date> ist. Lassen Sie uns versuchen, aber Ihre Unterschrift:

public static <T extends Comparable<T>> void sort(List<? extends T> list) {} 

List<java.sql.Date> list = new ArrayList<>(); 
sort(list); 

Hier T als java.util.Date rein zufällig. Die Java 7-Spezifikation erlaubt jedoch keinen solchen Rückschluss. Daher kann dieser Code mit Java-8 kompiliert werden, aber nicht, wenn sie unter Java-7 zusammengestellt:

Main.java:14: error: method sort in class Main cannot be applied to given types; 
     sort(list); 
     ^
    required: List<? extends T> 
    found: List<Date> 
    reason: inferred type does not conform to declared bound(s) 
    inferred: Date 
    bound(s): Comparable<Date> 
    where T is a type-variable: 
    T extends Comparable<T> declared in method <T>sort(List<? extends T>) 
1 error 

Typ-Inferenz stark in Java-8 verbessert wurde. Separate JLS chapter 18 ist jetzt dazu gewidmet, während in Java-7 die Regeln were viel einfacher sind.

+1

Aber wie funktioniert statisch > T max (Sammlung c) funktioniert gut ... ist das wieder ein Inferenzproblem? – prvn

+0

@prvn, hier haben Sie 'Comparable ' anstelle von 'Comparable ' wie in Ihrer vorgeschlagenen Unterschrift. '> void sort (Liste Liste)' würde auch funktionieren, aber hinzufügen '? extents T 'ist hier völlig unnötig, während es in der' max'-Methode sinnvoll ist (da 'max' den Wert zurückgibt). –

+6

Es gibt einen anderen Punkt. Wenn Sie einen Wildcard-Typ wie 'List list' haben, können Sie' list.set (index, list.get (anotherIndex)) 'nicht innerhalb der Methode ausführen, da Wildcard-Typen funktionieren. Dies kann mit einer internen Hilfsmethode umgangen werden, die '? erweitert T 'in eine andere Typvariable (oder durch Verwendung ungeprüfter Operationen, wie es interne Implementierungen oft tun), aber ein Typ ohne Platzhalter ist für eine Liste, die geändert werden soll, sauberer. – Holger

4

Sie unterscheiden sich, weil ? super T ist weniger restriktiv als T. Es ist ein Lower Bounded Wildcard (die verlinkte Java Tutorial sagt, teil)

Der Begriff List<Integer> ist restriktiver als List<? super Integer> weil der ehemalige Integer nur eine Liste der Typ übereinstimmt, während letztere eine Liste von jeder Art übereinstimmt, ist ein Supertyp von Integer.

Integer Ersetzen durch T und es bedeutet eine Toder a java.lang.Object.

+0

aber ich bin flexibler in der Argumentation von sort() ..using? extends, – prvn

+0

Ja, niemand hat das 'List 'ist das gleiche wie' List '. Die zweite Version verwendet 'extends' und die Frage ist, ob dies gleich ist. – JojOatXGME

+0

Ich glaube nicht, dass deine Antwort direkt die Frage betrifft. – yuxh

9
// 0 
public static <T extends Comparable<? super T>> void sort0(List<T> list) 

// 1 
public static <T extends Comparable<T>> void sort1(List<? extends T> list) 

Diese Signaturen unterscheiden, weil sie über die Beziehung zwischen Typ unterschiedliche Anforderungen stellen T und die Art Argument Comparable in der Definition von T.

zum Beispiel Angenommen, Sie diese Klasse haben:

class A implements Comparable<Object> { ... } 

Dann, wenn Sie

List<A> list = ... ; 
sort0(list); // works 
sort1(list); // fails 

Der Grund sort1 versagt haben, ist, dass es kein Typ ist T, die sowohl vergleichbar selbst und das ist oder ist ein Supertyp des Listentyps.

Es stellt sich heraus, dass die Klasse A fehlerhaft ist, weil Objekte, die Comparable sind, bestimmte Anforderungen erfüllen müssen. Insbesondere die Umkehrung des Vergleichs sollte das Vorzeichen des Ergebnisses umkehren. Wir können eine Instanz von A mit einer Object vergleichen, aber nicht umgekehrt, so dass diese Anforderung verletzt wird. Beachten Sie jedoch, dass dies eine Anforderung der Semantik von Comparable ist und nicht vom Typsystem auferlegt wird. Betrachtet man nur das Typsystem, sind die beiden sort Deklarationen wirklich unterschiedlich.