2014-12-26 15 views
5

Dlaczego Java może wnioskować o wspólnym przodku wielu typów o górnym ograniczeniu, ale nie o typach o dolnych ograniczeniach?Wnioskowanie typu Java o niższych ograniczonych typach

Dokładniej, należy rozważyć następujące przykłady:

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

Twój test kompiluje się dla mnie bez jawnego argumentu typu przy użyciu 1.8.0_25. – Alex

+0

@Alex Używam 1.7. Może zostało to naprawione. – shmosel

Odpowiedz

1

myślę, że mogę wyjaśnić dlaczego Java rozróżnia niższy typ ograniczony i górny.

Próba wywarcia wspólnej dolnej granicy może zakończyć się niepowodzeniem, gdy używane są niekompatybilne ograniczenia, na przykład Integer i Long. Kiedy używamy górnej granicy, zawsze można znaleźć wspólną górną granicę, w tym przypadku List<? extends Number>. Ale nie ma wspólnej dolnej granicy List<? super Integer> i List<? super Long>. Jedyną bezpieczną opcją w przypadku takiego konfliktu byłby zwrot List<? extends Object>, równoznaczny z List<?>, co oznacza "a List nieznanego typu".

Prawdopodobnie moglibyśmy tego użyć tylko wtedy, gdy rzeczywiście istnieją sprzeczne granice, w przeciwieństwie do przypadku z mojego pytania. Być może jednak zdecydowano się na łatwe wyjście i nie zakładać wspólnej dolnej granicy, chyba że jest to wyraźnie określone.

0

używam 1.8.0_25 i Dostaję błąd kompilacji. Błąd nie polega jednak na tym, że wywołanie do pobrania jest złe, ale zmienna, w której chcesz umieścić wynik. Powtarzając swój przykład:

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 
} 

Jeśli spojrzeć na to, jak T jest podstawiony w zaproszeniu, parametry są używane jako listy, utraty informacji o dolną granicę.

O wnioskowaniu: co? nie jest dokładnie "czymkolwiek można przypisać ...", ale "szczególnym typem, którego nie chcę nazwać, można przypisać do ...". Ma to znaczenie, ponieważ w twoim przykładzie otrzymujesz 3 zmienne, 1 dla każdej listy i inną, inną dla wyniku wyboru. Teraz, z powodu deklaracji pobrania, podstawienie T musi spełniać hierarchię klas parametrów. W pierwszym przypadku potrzebujesz substytutu dla < # 1 extends> i < # 2 extends Number>. # 2 może być Double, więc najlepszą wskazówką jest to, że # 3 rozszerza Number. W drugim przypadku potrzebujesz substytutu dla < # 1 superklinego> i < # 2 super numer>. Teraz oznacza to, że # 2 może być kimkolwiek z Numeru, Obiektu, Serializowalnym; # 1 dodaje do tej listy porównywalne i całkowite. Kombinacjami może być Number, Object (a T powinno być Object); lub Serializable, Integer (i T może być Serializable), więc najlepszą wskazówką jest to, że T jest Listą nieznanego typu rozszerzającego Obiekt.

Oczywiście może to osiągnąć tylko numer, ale nie można dostać dwie granice dla tego samego typu zmiennej, więc ma pozwolić jej na tym

+0

Nie widzę twojego rozumowania. Tak jak przesuniemy granicę do najbliższej wspólnej superklasy ('Number') z' extends', powinniśmy przenieść ją do najbliższej wspólnej podklasy ('Integer') z' super'. – shmosel

Powiązane problemy