Java wildcard super T w strumieniach - jak to działa

Java wildcard super T w strumieniach - jak to działa
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
0

Witam
załóżmy że mamy Liste Double

Kopiuj
List<Double> list = Arrays.asList(4.3, 2.7, 5.6);

I teraz mam pytanie odnośnie wildcardów. Wiemy że w strumieniach jak mamy filter to używamy Predicate<? super T>. Moje pytanie brzmi - czemu to właścwie działa.
Np.

Kopiuj
list.stream().filter(i -> i > 3.0).forEach(i -> System.out.println(i));

Z tego co wiem zapis: ? super T oznacza że dany obiekt jest albo klasy T albo jest klasa T rozszerza klase tego obiektu - może być to np. Object albo Number. Dlaczego właściwie kompilator sie nie rzuzic że coś nie halo, bo przeciez nie można wywołać Object.compareTo(3) :)
Wzywam @jarekr000000 @Shalom i @Koziołek


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
edytowany 1x, ostatnio: scibi92
Wibowit
  • Rejestracja:około 20 lat
  • Ostatnio:około 3 godziny
0

W interfejsie Predicate przecież nie ma metody compareTo tylko test. Predykatem dla Object albo Number możesz przetestować Double'a, więc filter pozwala na wrzucenie takiego predykatu.

Zabawa z super i extends w generykach ma związek z kowariancją i kontrawariancją.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 1x, ostatnio: Wibowit
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
0

@Wibowit: no w sumie racje, to był taki troche skrót myślowy. Jest test, ale ten test teoretycznie można wywołac na instacji Object a przecież Object nie implementuje compareTo. Super to ograniczenie z góry nie z dołu, stąd tego nie rozumiem. Pojęcia Covariance i Covariant tez nie są mi obce :) (chociaz brzmią tak skomplikowanie że mam problemy z ich zapamiętaniem :D )


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
edytowany 1x, ostatnio: scibi92
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 6 godzin
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4708
1

Nie można wywołać "podając" Object jako instancję, albowiem twoja lista ma obiekty typu T. I inne nie wpadną nigdy jako argumenty metody test (Predykatu). Bo lista dysponuje tylko takimi (T ).
Natomiast jeśli zapodasz Predicate<Object> to ten predykat nadal bedzie działał dobrze z twoją listą elementów typu T. Bo T jest również typu Object. Ot i cała magia kontrwariancji.


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 4x, ostatnio: jarekr000000
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
0

@jarekr000000: no własnie tak żem czuł że chodzi o to że jak mamy streamy to nie ma możliwości wywołania tego na innym typie niż to ograniczenie z góry czyli T :)
Ale wolałem sie upewnić że mój tok rozumowania jest prawidłowy ;)


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
Wibowit
  • Rejestracja:około 20 lat
  • Ostatnio:około 3 godziny
1

Gdyby filter przyjmował Predicate<T> to nie mógłbyś wykorzystać Predicate<Object> na strumieniu np Stringów. Strumień stringów wymagałby tylko i wyłącznie Predicate<String>. Dzięki temu, że filter przyjmuje Predicate<? super T> to możesz użyć Predicate<Object> na wszystkich typach strumieni. To ? super T zamiast zwykłego T daje ci taką możliwość - nie ogranicza możliwości strumienia, tylko je rozszerza.

Ma to w końcu sens dla ciebie?

Podobnie w klasie java.util.Collections mamy metodę:

Kopiuj
public static <T extends Comparable<? super T>> void sort(List<T> list)

Dzięki takiej sygnaturze może mieć np LIstę obiektów typu X, gdzie X roszerza Y i implementuje Comparable<Y>. Porównywacz obiektów typu Y poradzi sobie z obiektami typu X - a odwrotnie nie.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 4x, ostatnio: Wibowit
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
0

@Wibowit: oczywiście masz racje. Tylko mi chodziło o to czemu kompilator nie ma pretensji i moge wywołac metody z klasy ktora jest górnym ograniczeniem Nie mniej jednak @jarekr000000 utwierdził mnie w przekonaniu że wynika to z faktu że jesli mamy Stream<T> to kompilator będzie wiedział że na pewno predicate będzie wywołany na T a nie jego rodzicu :)


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
Wibowit
  • Rejestracja:około 20 lat
  • Ostatnio:około 3 godziny
0

Możesz to rozumieć tak, że jeśli generyczny typ jest postaci ? extends X to znaczy, że metoda ma być poprawna dla każdego typu, który jest podklasą X, bądź jest tożsamy X. Analogcznie dla ? super X - wtedy metoda ma być poprawna dla każdego typu, który jest nadklasą X, bądź jest tożsamy X.

W przypadku filter mamy Predicate<? super T>. Oznacza to, że dla każdego typu będącego nadklasą T, bądź tożsamego T metoda ma być poprawna. Chyba nietrudno wydedukować, że filter na Stream<T> zadziała na Predicate<T>, Predicate<Object> czy Predicate<U> dla każdego typu U będącego nadklasą T.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
S9
Ale ja przecież wiem co to znaczy ? extends T i ? super T :D
Wibowit
no to co wzbudza zdziwienie w takim razie?
Wibowit
  • Rejestracja:około 20 lat
  • Ostatnio:około 3 godziny
0

Postaram się wyjaśnić sprawę łopatologicznie. Pytajnik oznacza inne rzeczy w zależności od której strony go używamy, tzn czy wrzucamy jakiś obiekt do metody jako parametru z pytajnikiem czy może korzystamy z takiego parametru w metodzie. Konkretnie to jeśli mamy Stream<T> i metodę filter(Predicate<? super T> predicate) to:

  • w miejscu podawania argumentu do takiej metody możemy podać Predicate<U> dla dowolnego U będącego albo nadklasą T albo tożsame T
  • w miejscu korzystania z argumentu typu Predicate<? super T> nasz kod musi działać dla każdego możliwego U (z punktu poprzedniego) jednocześnie. Jeśli interfejs Predicate<T> ma metodę test(T t) to w miejsce t możemy podać argument typu T lub podklasę T. Tak samo jest jeśli mamy parametr typu Predicate<? super T>, bo argument typu T lub będący podklasą T pasuje (jako argumentu metody test) dla każdego typu, który spełnia ograniczenie (tak jak typ U z poprzedniego punktu)

Zauważ, że parametry metody są kontrawariantne, a rezultaty metod są kowariantne. C# i Scala wariancję oznaczają w miejscu definicji klasy:
https://msdn.microsoft.com/pl-pl/library/bb549151(v=vs.110).aspx (słówka in i out przy typach generycznych)
https://www.scala-lang.org/api/current/scala/Function1.html (plusik i minusik przy typach generycznych)
W Javie wariancję oznacza się w miejscu użycia klasy (co jest zwykle mniej czytelne) poprzez te ograniczenia ? super T czy ? extends T.

Chyba jednak poległem w tłumaczeniach, a jest już późno. Polecam poczytanie konkretnych artykułów o wariancji. Strona na Wiki którą wcześniej podałem jest raczej dobrym punktem startowym.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
S9
Ale ja już napisałem że rozumiem o co chodzi :D

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.