2015-07-04 18 views
5

Problem jest w sposób ogólny ograniczeń:Czy możliwe jest zaimplementowanie metody z listą podpisów <klasa <? rozszerza Adnotacje >> w Javie?

public List<Class<? extends Annotation>> getAnnotations() { 
    return new ArrayList<>(Arrays.asList(Override.class)); 
} 

typ realny zwrot jest ArrayList<Class<Override>>
Metoda spodziewa List<Class<? extends Annotation>>

Class<Override> jest podtypem Class<? extends Annotation>
Class<? extends Annotation> c = Override.class; // dozwolone

ArrayList jest podtyp List, jeśli typy elementów pasują do siebie:
List<? extends Number> l = new ArrayList<Integer>(); // dozwolone

Jednak nie jest dozwolone: ​​

List<Class<? extends Annotation>> l = Arrays.asList(Override.class); 
List<Class<? extends Annotation>> l = new ArrayList<>(Arrays.asList(Override.class)); 

Czy to w ogóle możliwe, czy Class symbole wieloznaczne są łamane?

+0

@kocko jakiego używałeś java? Mam jdk1.7_079, oto zrzut ekranu https://www.dropbox.com/s/p6gybp1jct19ehg/generics.jpg – AdamSkywalker

+1

Grałem z tym trochę i myślę, że znalazłem przyczynę. Napiszę odpowiedź po chwili. –

+1

Z testowania, wygląda jak w Java 7, musisz sparametryzować wywołanie statyczne 'Tablice. > asList (Override.class); '. Z Java 8 nie potrzebowałem –

Odpowiedz

2

Zakładam, że dzieje się tak z powodu natury wnioskowania typu jdk 1.7.

Jak być może wiesz już, metoda Arrays.asList(T ... elems) jest nazwą rodzajową, ale rzadko wyraźnie określić typu parametru, który chcielibyśmy metody do pracy i mamy zatem polegać na funkcję rodzaj wnioskowania kompilatora.

Więc, gdy kompilator widzi oświadczenie Arrays.asList(Override.class) będzie wywnioskować, że parametr typu dla metody należy zastąpić Class<Override>, to znaczy, że mamy wersję metody w tej formie:

public List<Class<Override>> asList(Class<Override> ... elems) 

jednak jeśli jawnie ustawić parametr typu dla metody

List<Class<? extends Annotation>> l = 
       Arrays.<Class<? extends Annotation>>asList(Override.class); 

następnie kompilator będzie rzeczywiście wiedzieć, co typu parametr musi być zastąpiony, a następnie wersja metody .asList() byłoby:

public List<? extends Annotation> asList(Class<? extends Annotation> ... elems) 

Teraz będzie to skompilować grzywny, ponieważ Class<? extends Annotation> jest kompatybilny do Class<Override>. W języku Java 8 funkcja wnioskowania o typ jest jeszcze bardziej ulepszona, dzięki czemu nie trzeba jawnie ustawiać parametru typu dla metody .asList().

Jednak więcej ciekawe pytanie idzie do

Dlaczego List<Class<Override>> nie jest kompatybilny z List<Class<? extends Annotation>>?

The java.lang.Class jest final jeden, który pomoże odpowiedzieć na następujące dwa pytania, kombinacja, która odpowie na powyższe pytanie.:)

Więc

  • Co robi List<Class<Override>> oznacza?

List<Class<Override>> oznacza, że ​​możemy dodać tylko instancje Class<Override> i nic innego do listy. Co jest wspaniałe, wiedząc, że nie możemy nawet dodać podklas Class<Override>, ponieważ typ Class to final.

  • Co oznacza List<Class<? extends Annotation>>?

Ten typ List reprezentuje cały rodzinę list klas, z których wszystkie są podklasy rodzaju Annotation, co oznacza, że ​​możemy z powodzeniem dodać dowolny typ adnotacji (na przykład, SuppressWarnings.class, Override.class, Documented.class itp.) do listy.

Pozwala przypuszczać, że następujący przykład był rzeczywiście poprawne:

List<Class<Override>> overrides = Arrays.asList(Override.class); 
List<Class<? extends Annotation>> annotations = new ArrayList<>(); 
annotations = overrides; 
annotations.add(SuppressWarnings.class); //HUGE PROBLEM 
annotations.add(Documented.class); //ANOTHER HUGE PROBLEM 

Dwa ogromne problemy pochodzą z faktu, że próbujesz dodać pewne dopuszczalne Override instancji do overrides, co jest bardzo złe.

Mamy wystarczająco inteligentny kompilator, który może rzeczywiście wykryć takie potencjalne problemy, a wyrzucenie błędu podczas kompilacji jest sposobem, aby uniemożliwić nam to.

Więcej informacji:

+0

dzięki za poświęcony czas. pierwsza część mówi, że właśnie dostaliśmy sztuczkę z wykrywaniem typu. o ile mi wiadomo, jest zmiana w Java8, która analizuje lewą część instrukcji assign, więc rozwiązuje ten problem. druga część odnosi się do klasycznego problemu z podtypami w kolekcjach, jestem tego obeznany. Poczekajmy kilka dni, żeby zobaczyć, czy coś tu nie ma, a jeśli wszystko jest w porządku, zaakceptuję to. jeszcze raz dziękuję :) – AdamSkywalker

+0

Nie ma za co. :) –

1

ArrayList jest podtypem z listy, jeśli typy elementów pasuje:

List<? extends Number> l = new ArrayList<Integer>(); // allowed 

Tak, ale w swojej przykład typy elementów nie pasuje:

List<Class<? extends Annotation>> l = new ArrayList<Class<Override>>(); 

prawda, Class<Override> jest podtypem Class<? extends Annotation>, ale podobnie jak List<String> nie jest podtypem List<Object>, List<Class<Override>> nie jest podtypem List<Class<? extends Annotation>>. Byłby to jednak podtyp List<? extends Class<? extends Annotation>>.

To powiedziawszy, powodem, dla którego twój kod się nie kompiluje, jest to, że wnioskowanie typu nie uwzględnia typu zwracanego w instrukcji Java w typie Java 7, więc domyślnie jest to najbardziej specyficzny typ, może być przypisany do

Arrays.asList(Override.class) 

nie zdając sobie sprawy, że oświadczenie powrót byłby skompilować tylko z bardziej elastycznego typu (Java 8 typ wnioskowanie jest mądrzejszy, btw).Jednym rozwiązaniem jest jawnie określić argument typu:

Arrays.<Class<? extends Annotation>(Override.class); 

lub dać Java 7 typ wnioskowania podpowiedź poprzez przypisanie do zmiennej lokalnej pierwszy:

List<Class<? extends Annotation>> list = Arrays.asList(Override.class); 
return list; 

lub zmienić typ metoda powrotu do

List<? extends Class<? extends Annotation>> getAnnotations() 

, więc sugerowany typ nie ma znaczenia.

+0

ostatni wiersz kodu wygląda niedorzecznie, ale działa, lol – AdamSkywalker

Powiązane problemy