Generics - extends

0

Mam pytanie w sprawie Generics. Jak wiadomo istnieje konstrukcja taka:

List<? extends X> ...

Czyli jest to lista do której można wkładać obiekty klas rozszerzająych klasę X.

Ale przecież do listy zadeklarowanej w taki sposób "List<X>" też możemy wkładać obiekty klas rozszerzających klasę X.

Jaki jest wobec tego sens tego zapisu "? extends X"?

Posłużę się przykładem:

import java.util.ArrayList;
import java.util.List;

public class Generics_Test {

    public static void main(String[] args) {
        List<Vehicle> lv = new ArrayList<Vehicle>();
        lv.add(new Car("Mercedes"));

        doSth(lv);
    }

    static void doSth(List<? extends Vehicle> lv){
        for (Vehicle vehicle : lv) {
            System.out.println(vehicle.getName());
        }

        // lv.add(new Car("BMW")); nie kompiluje się
    }


    private static interface Vehicle {
        String getName();
    }

    private static class Car implements Vehicle {
        private final String name;

        public Car(String name) {
            this.name = name;
        }
        @Override
        public String getName() {
            return name;
        }
    }
}

 

W metodzie "doSth" odbieram listę jako "List<? extends Vehicle>". Przeglądanie listy jest możliwe, wyciąganie z niej elementów typu Vehicle. Ale już dodanie do takiej listy nowego obiektu typu Car (który przecież spokojnie został dodany w metodzie main) się nie udaje. Żeby ten kod się skompilował musiałbym zmienić ten zapis i napisać "List<Vehicle>", tak samo jak w metodzie main.

Czy ktoś mi może pomóc to zrozumieć, ewentualnie skierować do dogrego źródła tłumaczącego tą różnicę?

Z góry dzięki
Pozdrawiam
Mietek

0

Ale zdajesz sobie sprawę że to co pokazałeś i to o co pytasz nie ma ze sobą nic wspólnego?
Chodzi tu o kowiariancje/kontrawariancje typów generycznych. Konkretnie o to że takiego List<String> nie możesz przypisać do List<Object>.

0

Taki zapis <? extends Vehicle> jest pewną małą pułapką językową. Znak ? jest znakiem specjalnym w kontekście typów generycznych oznaczającym nie tyle co "cokolwiek", a "diabli wiedzą co". Przez to kompilator nie może wywnioskować z czym będzie miał do czynienia i sypie błędami.

0

Dobrze myśleć o tym używając "Get and Put Principle".
Np. tu jest to tym: http://codeidol.com/java/javagenerics/Subtyping-and-Wildcards/The-Get-and-Put-Principle/

Mechanizm ma służyć większej elastyczności systemu typów, bo np. taką metodę z Collections można zastosować w znacznie większej ilości przypadków, niż gdyby była zrobiona bez 'wildcards':
public static <T> void copy(List<? super T> dest, List<? extends T> src)

0

W ogólności kod gdzie jest "? extends/ super X" ma być poprawny dla dowolnego podstawienia pytajnika zgodnego z ograniczeniem. Dlatego np mając kolekcję typu 'List<? extends Vehicle>' można z niej wyciągnąć Vehicle, ale nie można do niej niczego włożyć. Nie można, bo np podstawiając za ? pewną mało znaną podklasę Vehicle i próbując wstawić do listy inną podklasę, która nie ma nic wspólnego z tą mało znaną, stworzylibyśmy niepoprawny kod. Analogicznie, mając kolekcję typu 'List<? super Vehicle>', możemy do niej wkładać Vehicle i podklasy Vehicle, ale nie możemy z niej wyciągać Vehicle. Nie możemy, bo pewnym poprawnym podstawieniem ? jest tutaj Object (bo Object jest nadklasą Vehicle), a więc lista teoretycznie mogłaby przechowywać cokolwiek (gdyby była sparametryzowana jako List<Object>).

W tym konkretnym przypadku (z posta początkowego) 'lv.add(new Car("BMW"))' nie kompiluje się, bo nie będzie to poprawnym zapisem dla dowolnego podstawienia pytajnika zgodnego z ograniczeniem. Dla przykładu Car może dziedziczyć prosto po Vehicle, ale Lista może być typu List<Bicycle> i dodanie Car do Listy Bicycle'ów oczywiście nie będzie poprawne.

0

Wszystkie te tłumaczenia skraca obrazek z tego tutoriala http://docs.oracle.com/javase/tutorial/java/generics/subtyping.html

1 użytkowników online, w tym zalogowanych: 0, gości: 1