2011-06-28 18 views
6

Mam następujące klasy:inny java pytanie rodzajowy

interface Able{/* ... */} 
class A implements Able{/* ... */} 

i mam

Map<String,? extends Able> as; 
as = new HashMap<String, A>(); 

dlaczego następuje powodować błąd:

as.put("a", new A()); 

jakieś pomysły?

+2

co jest dokładnie taki błąd? – Bohemian

Odpowiedz

15

Odwołanie do generics java jest dobre (jdk site).

Rzeczywiście @Oli_Charlesworth dał dobrą odpowiedź, ale być może ta będzie bardziej kompletna.

W Collection<? extends Able> nie można wstawić niczego, co jest poprawne.

Jeśli masz

class A implements Able {...} 

i

class B implement Able {...} 

Następnie Collection<? extends Able> jest super typ zarówno:

Collection<A> 
Collection<B> 

Tak więc jest to legalne napisać oświadczenie jak

//Code snippet 01 
Collection< ? extends Able > list; 
Collection<A> listA; 
Collection<B> listB; 
list = listA; 
list = listB; 

Jest to rzeczywiście powód, dla którego istnieje symbol wieloznaczny Collection<? extends Able>.

Ale tutaj rzeczy są coraz bardziej interesujące:

W Collection<A> można wstawić tylko obiekty, które są A (w tym podklasy). To samo dla Collection<B>. W obu nie można dodać czegoś, co jest tylko Able. Na przykład:

//Code snippet 02 
listA.add(new A()); //valid at compile-time 
listA.add(new B()); //not valid at compile-time 
listB.add(new B()); //valid at compile-time 
listB.add(new A()); //not valid at compile-time 

Tak więc, jeśli grupa co widzieliśmy w code snippets 01 & 02, można zrozumieć, że jest to absolutnie niemożliwe, aby kompilator zaakceptować oświadczenie jak:

Collection< ? extends Able > list; 
list.add(new A());   //not allowed, will work only if list is List<A> 
list.add(new B());   //not allowed, will work only if list is List<B> 

więc tak, super typ Collection< ? extends Able > nie akceptuje dodawania niczego. Bardziej ogólne typy oferują przecięcie funkcjonalności podtypów i jako takie mniej funkcji podtypu. Tutaj tracimy możliwość dodawania obiektów A i obiektów B. Tych funkcji nastąpi później w hierarchii ... i to jeszcze nie oznacza, że ​​możemy dodać coś w super klasie Collection< ? extends Able >

Dodatkowa uwaga:

Należy również pamiętać, że w Collection<Able> można dodać cokolwiek chce tak:

Collection<Able> list; 
list.add(new A());   //valid 
list.add(new B());   //valid 

Ale Collection<Able> nie jest nadklasą Collection<A> i Collection<B>. Oznaczałoby to, podobnie jak w przypadku każdej relacji dziedziczenia, że ​​podklasy mogą robić, co może zrobić ich nadklasa, ponieważ dziedziczenie jest specjalizacją. Oznacza to, że możemy dodać obiekty Object i B do obu podklas Collection<A> i Collection<B>, a tak nie jest. Tak, to nie jest nadklasą nie można mieć:

Collection<Able> list; 
Collection<A> listA; 
Collection<B> listB; 
list = listA; //not valid because there is no inheritance hierarchy 
list = listB; //not valid because there is no inheritance hierarchy 

Zauważ, że dziedziczenie jest hyperonimic relacja (uogólnienie/specjalizacja) oraz zbiory zdefiniować meronimic relację (pojemnik/containee). I ból głowy polega na formalnym połączeniu obydwu form, mimo że jest on dość łatwo wykorzystywany przez niewyraźne istoty, na przykład we francuskiej wersji językowej: synecdocque. :)

+0

Twoja odpowiedź jest więcej niż doskonała, dlaczego nie mogę dodać instancji A do kolekcji , chociaż A jest w rzeczywistości typu Able ... Gdzie jest tu typ un-safety here? Thnx :) – elmorabea

8

Od http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html:

There is, as usual, a price to be paid for the flexibility of using wildcards. That price is that it is now illegal to write into [a wildcard-based container]. For instance, this is not allowed:

public void addRectangle(List<? extends Shape> shapes) { 
    shapes.add(0, new Rectangle()); // Compile-time error! 
} 

You should be able to figure out why the code above is disallowed. The type of the second parameter to shapes.add() is ? extends Shape -- an unknown subtype of Shape . Since we don't know what type it is, we don't know if it is a supertype of Rectangle ; it might or might not be such a supertype, so it isn't safe to pass a Rectangle there.

0

Nie można wstawić dowolny obiekt dowolnego typu w kolekcji zadeklarowane za pomocą dziką kartę „?”

można wstawić tylko „null”

Po zadeklarować kolekcji w formie listy, kompilator nie może wiedzieć, że to jest bezpieczne, aby dodać SubAble.

Co się stanie, jeśli Collection<SubSubAble> zostało przypisane do Collection<Able>? Byłoby to ważne zadanie, ale dodanie SubAble spowodowałoby zanieczyszczenie kolekcji.

How can elements be added to a wildcard generic collection?

+0

"Co, jeśli kolekcja została przypisana do SubAble?" Co to znaczy? –

+0

Przepraszam, trzeba oznaczyć je jako sekcję kodu, inaczej części generyczne nie są wyświetlane – fmucar

0

deklaracja

Map<String,? extends Able> as; 

oznacza "jakąkolwiek mapę, z kluczami ciąg i wartości są podtypem móc". Tak więc, na przykład, można wykonać następujące czynności:

Map<String,? extends Able> as = new HashMap<String, SubSubAble>(); 

A teraz spójrzmy na ten przykład:

Map<String,? extends Able> as = new HashMap<String, SubSubAble>(); 
as.put("key", new A()); 

Gdyby to było prawidłowe, skończysz mający HashMap z zawartością { „klucz”, new A()} - czyli błąd typu!

0

Collection<?> to supertyp dla wszystkich rodzajów kolekcji. To nie jest kolekcja, która może pomieścić każdy typ. Przynajmniej to było moje niezrozumienie całej koncepcji.

Możemy używać go gdzie nie dbamy o typ rodzajowy, jak w poniższym przykładzie:

public static void print(Collection<?> aCollection) { 
    for (Object o:aCollection) { 
    System.out.println(o); 
    } 
} 

Jeśli wybraliśmy podpis Zamiast:

public static void print(Collection<Object> aCollection) 

my by ograniczyłem się do kolekcji typu Collection<Object> - innymi słowy, taka metoda nie akceptuje wartości typu Collection<String>.

więc rodzajem Collection<?> jest nie zbiór, który może trwać wszelkiego rodzaju. Zajmuje tylko nieznany typ.A ponieważ nie znamy tego typu (jego nieznane;)), nigdy nie możemy dodać wartości, ponieważ żaden typ w java nie jest podklasą nieznanego typu.

Jeśli dodamy ograniczenia (takie jak <? extends Able>), typem jest wciąż nieznany.

Poszukujesz deklaracji mapy, której wartości implementują interfejs Able. Prawidłowe zgłoszenie jest po prostu:

Map<String, Able> map; 

Załóżmy, mamy dwa rodzaje A i B że podklasa Able i dwie dodatkowe mapy

Map<String, A> aMap; 
Map<String, B> bMap; 

i chcesz, metoda, która zwraca dowolną mapę, którego wartości wdrożenia Able interfejs: następnie używamy wieloznaczny:

public Map<String, ? extends Able> createAorBMap(boolean flag) { 
return flag ? aMap: bMap; 
} 

(ponownie z ograniczeniem, że nie możemy dodać nowych par klucz/wartość do mapy zwróconej przez tę metodę).

3

Dobrym sposobem na zrozumienie tego problemu jest, aby przeczytać, co wieloznaczny oznacza: „mapa z klawiszami typu String i wartości jednego typu, która rozciąga się w stanie”

Map<String,? extends Able> as; 

Powodem, dla którego operacje dodawania są niedozwolone, jest fakt, że "otwierają drzwi", aby wprowadzić różne typy w kolekcji, które byłyby sprzeczne z systemem pisania. np.

class UnAble implements Able; 
Map<String,UnAble> unableMap = new HashMap<String,UnAble>(); 
Map<String,? extends Able> ableMap = unableMap; 
ableMap.put("wontwork",new A()); // type mismatch: insert an A-type into an Unable map 

Prawidłowe korzystanie z budową wieloznacznym byłoby:

Result processAble(Map<String,? extends Able>) { ... read records & do something ... } 

Map<String,A> ableMap = new HashMap<String,A>; 
ableMap.put("willwork",new A()); 
processAble(as); 
processAble(unableMap); // from the definition above