2014-12-26 8 views
5

Warum kann Java auf den gemeinsamen Vorfahren mehrerer oberer begrenzter Typen, nicht aber auf weniger beschränkter Typen schließen?Java-Typ-Inferenz mit unteren beschränkten Typen

Insbesondere sollten Sie die folgenden Beispiele:

static class Test { 

    static <T> T pick(T one, T two) { 
     return two; 
    } 

    static void testUpperBound() { 
     List<? extends Integer> extendsInteger = new ArrayList<>(); 

     // List<? extends Integer> is treated as a subclass of List<? extends Number> 
     List<? extends Number> extendsNumber = extendsInteger; 

     // List<? extends Number> is inferred as the common superclass 
     extendsNumber = pick(extendsInteger, extendsNumber); 
    } 

    static void testLowerBound() { 
     List<? super Number> superNumber = new ArrayList<>(); 

     // List<? super Number> is treated as a subclass of List<? super Integer> 
     List<? super Integer> superInteger = superNumber; 

     // The inferred common type should be List<? super Integer>, 
     // but instead we get a compile error: 
     superInteger = pick(superNumber, superInteger); 

     // It only compiles with an explicit type argument: 
     superInteger = Test.<List<? super Integer>>pick(superNumber, superInteger); 
    } 
} 
+0

Ihr Test kompiliert für mich ohne das Argument explizite Typ mit 1.8.0_25. – Alex

+0

@Alex Ich benutze 1.7. Vielleicht ist es seither behoben worden. – shmosel

Antwort

1

ich glaube, ich kann erklären, warum Java zwischen einem Nieder- unterscheidet begrenzter und oberer begrenzter Typ.

Der Versuch, auf eine gemeinsame untere Grenze zu schließen, kann fehlschlagen, wenn inkompatible Grenzen verwendet werden, z. B. Integer und Long. Wenn wir eine obere Grenze verwenden, ist es immer möglich, eine gemeinsame obere Grenze zu finden, in diesem Fall List<? extends Number>. Aber es gibt keine gemeinsame untere Grenze von List<? super Integer> und List<? super Long>. Die einzige sichere Option im Falle eines solchen Konflikts wäre die Rückgabe List<? extends Object>, gleichbedeutend mit List<?>, was "eine List unbekannten Typs" bedeutet.

Nun hätten wir wohl nur dann darauf zurückgreifen können, wenn es tatsächlich widersprüchliche Grenzen gäbe, im Gegensatz zu dem Fall in meiner Frage. Aber vielleicht wurde beschlossen, den einfachen Ausweg zu nehmen und nicht anzunehmen, dass es eine gemeinsame untere Schranke gibt, wenn nicht ausdrücklich angegeben.

0

Ich verwende 1.8.0_25 und ich erhalte die Compiler-Fehler. Der Fehler ist jedoch nicht, dass der Aufruf zur Auswahl schlecht ist, sondern die Variable, in die das Ergebnis eingefügt werden soll. Wiederholung Ihr Beispiel:

static void testLowerBound() { 
    List<? super Number> superNumber = new ArrayList<>(); 
    List<? super Integer> superInteger = superNumber; 

    // this gets the error 
    superInteger = pick(superNumber, superInteger); 
    // this doesn't 
    pick(superNumber, superInteger); 

    // what's happening behind is 
    List<? extends Object> behind = pick(superNumber, superInteger); 
    superInteger = behind; 
    // that last line gets the same compilation error 
} 

Wenn man sich anschaut, wie T in dem Aufruf ersetzt wird, werden die Parameter als Liste verwendet, die Informationen über die untere Grenze zu verlieren.

Über Schlussfolgerung: Jeder? ist nicht genau "was auch immer dem zugewiesen werden kann ...", sondern "ein bestimmter Typ, den ich nicht benennen möchte, der zugewiesen werden kann ...". Es spielt eine Rolle, denn in Ihrem Beispiel erhalten Sie 3 Variablen, 1 für jede Liste und eine andere, andere für das Ergebnis der Auswahl. Nun muss die Substitution für T aufgrund der Pick-Angabe die Klassenhierarchie der Parameter erfüllen. Im ersten Fall benötigen Sie einen Ersatz für < # 1 erweitert Integer> und < # 2 erweitert Nummer>. # 2 könnte Double sein, also ist der beste Hinweis, den du hast, dass # 3 Nummer ausdehnt. Im zweiten Fall benötigen Sie einen Ersatz für < # 1 Super Integer> und < # 2 Super Number>. Nun, das bedeutet, # 2 könnte jemand von Number, Object, Serializable sein; # 1 fügt dieser Liste Comparable und Integer hinzu. Die Kombinationen könnten Nummer, Objekt (und T sollte Objekt sein); oder Serializable, Integer (und T könnte serialisierbar sein), so dass der beste Hinweis darauf ist, dass T eine Liste eines unbekannten Typs ist, der das Objekt erweitert.

Natürlich konnte es nur auf Nummer erreichen, aber man kann nicht zwei Grenzen für die gleiche Art Variable erhalten, so hat es zu lassen, an diesem

+0

Ich sehe deine Argumentation nicht. Genau wie wir die Grenze zur nächsten gemeinsamen Oberklasse ('Number') mit' extends' verschieben, sollten wir sie mit 'super' zur nächsten gemeinsamen Unterklasse (' Integer') verschieben. – shmosel