Jaka jest roznica miedzy Pair<?>
a Pair <T>
?
Jaka jest roznica miedzy Pair<? extends Employee>
a Pair<T extends Employee>
?
Dzięki wildcardom można mieć np. heterogeniczne listy z kontrolą typów (chociaż nawet zbyt ograniczającą):
package org.example;
import java.util.ArrayList;
import java.util.List;
public class Wildcards {
static class Aaa<T> {
T get() {
return null;
}
void set(T input) {
}
}
public static void main(String[] args) {
List<Aaa<?>> heterogeneous = new ArrayList<>();
heterogeneous.add(new Aaa<Integer>());
heterogeneous.add(new Aaa<String>());
processHeterogeneousList(heterogeneous);
var item0 = heterogeneous.get(0);
var item1 = heterogeneous.get(1);
// item0.set(item0.get()); // won't compile, but seems too restrictive
// item0.set(item1.get()); // won't compile, OK because types are unrelated
// item1.set(item0.get()); // won't compile, OK because types are unrelated
// item1.set(item1.get()); // won't compile, but seems too restrictive
}
static void processHeterogeneousList(List<Aaa<?>> heterogeneous) {
for (Aaa<?> elem : heterogeneous) {
// elem.set(elem.get()); // won't compile, but seems too restrictive
getAndSet(elem); // passing Aaa<?> as Aaa<T> lifts restrictions from above comment
}
}
static <T> void getAndSet(Aaa<T> obj) {
obj.set(obj.get());
}
}
Zaawansowany ficzur. Żaden korpo-javowiec tego nie używa. Z drugiej strony wildcardy pojawiają się w bibliotece standardowej, np. w java.util.Collections#sort(java.util.List<T>)
public static <T extends Comparable<? super T>> void sort(List<T> list)
Spróbuję zrobić coś szalonego z tą metodą, ale nie wiem na ile są szanse, że mi się uda.
Aktualizacja: a może jednak nie będę robił nic szalonego :) Popatrzcie na te przykłady: https://stackoverflow.com/a/51682457 https://stackoverflow.com/a/35715359
@Ro3ert:
Zgłębiasz bo masz legacy projekt np. Java 1.3?
To polecam https://www.amazon.com/Java-Generics-Collections-Development-Process/dp/0596527756
Maurice Naftalin, Java Generics and Collections
Jak "Czesc, ucze sie programowac w springu," (styczeń 2021)
To polecam https://www.amazon.com/Effective-Java-Joshua-Bloch/dp/0134685997/
Joshua Bloch, Effective Java
Więcej jak zrozumienie PECS (Producer Extends, Consumer Super) nie będzie ci potrzebne
Klasa Animal, dziedziczą po niej klasy Cat i Dog
public class Animal {
@Override
public String toString() {
return getClass().getSimpleName();
}
}
public class Cat extends Animal {}
public class Dog extends Animal {}
Metoda kopiowania z jednej kolekcji do drugiej
public <T> void copy(Collection<? extends T> src, Collection<? super T> dst) {
dst.addAll(src);
}
Przykład PECS
public static void main(String[] args) {
List<Cat> catList = new ArrayList<>();
List<Animal> animalList = new ArrayList<>();
catList.add(new Cat());
animalList.add(new Animal());
animalList.add(new Cat());
animalList.add(new Dog());
for (Animal animal : animalList) {
System.out.println(animal);
}
for (Cat cat : catList) {
System.out.println(cat);
}
System.out.println();
copy(catList, animalList);
System.out.println(animalList);
}
private static <T> void copy(Collection<? extends T> src, Collection<? super T> dst) {
dst.addAll(src);
}
Producer - lista kotów - ma być skopiowana do innej listy.
Jakie warunki?
Można dołączyć koty z listy src do kotów na liście kotów dst
albo
Można dołączyć kory z listy src do kotów na liście określonej ogólnie jako zwierzęta dst
copy(animalList, catList);
nie skompiluje się.
Bo nie można z listy zawierającej ogólnie zwierzęta np psy dodać wszystko jak leci do listy która jasno jest określona "tylko koty" (pies z kotem nie zgodzą się).
Producer extends Cat (koty lub podklasa koty perskie)
Consumer super Cat (też koty albo ogólnie zwierzęta bo zapewniono, że nie ma znaczenia, że są razem - np. osobne klatki)
Z typowych przykładów codziennego użytku jeszcze
<T extends Comparable<? super T>>
/ https://docs.oracle.com/javase/8/docs/api/java/util/class-use/Comparator.html /
I wystarczy ;)
Tu masz wszystko opisane z przykładami, wróć z konkretnym problemem, niektórzy mają chyba za dużo wolnego czasu, że tworzą na forum własne tutoriale :) https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html
Suma liczb
final List<? extends Number> numbers = List.of(1, 2L, 3.0, 4.0F, 0x5);
final double total = total(numbers);
final double weirdTotal = weirdTotal(numbers);
Po Bożemu
private static <T extends Number> double total(Collection<T> numbers) {
double total = 0.0;
for (T number : numbers) {
total += number.doubleValue();
}
return total;
}
Albo iść w tutoriale sun/oracle
private static double weirdTotal(Collection<? extends Number> numbers) {
return helper(numbers);
}
private static <E extends Number> double helper(Collection<E> numbers) {
double total = 0.0;
for (E number : numbers) {
total += number.doubleValue();
}
return total;
}
Ale po co?
Przyjdzie najbardziej pożądany przez pracodawców programista w 2021 roku - fullstack - i powstanie pytanie, na czym się zacznie wykładać, bo coś jest przekombinowane?
Przyszedł z frontendowymi korzeniami to na cudach Java "bo się SonarLint pluje"
Przyszedł z Javą we krwi to zrobi cuda i sztuczki a na barddziej frontendowej części będą cuda.
Keep It Simple Sweetheart
? to typ nieznany.
T to typ ograniczony.
Np. moze byc
Pair<T>
T jeden;
T dwa;
Deklarujac np.
Pair<JakisObiekt>
bedzie to:
JakisObiekt jeden;
JakisObiekt dwa;
Beda to te same typy.
Nie zadeklarujesz:
? jeden;
? dwa;
Ale zadeklarujesz juz:
T jeden;
E dwa;
T mozesz zwrocic:
return T;
A ? nie zwrocisz.
@zgrzyt:
Typy wieloznaczne (wildcardy, ?
) istnieją po to, by zwiększyć możliwości generyków. Same w sobie (tzn. nie będące parametrami typów generycznych) nie mają sensu, bo można je zastąpić konkretnym typem nadrzędnym, czyli java.lang.Object
.
? metoda() {
? a = ...;
? b = ...;
return połącz(a, b);
}
Zamiast powyższego możesz zrobić poniższe bez żadnej straty ogólności:
Object metoda() {
Object a = ...;
Object b = ...;
return połącz(a, b);
}
Rozsądne jest odrzucanie pierwszej składni przez kompilator, bo jest nadmiarowa, a wildcardy są rzadko spotykane i rzadko rozumiane.
Natomiast List<Aaa<Object>>
i List<Aaa<?>>
to dwa zupełnie różne typy. Do pierwszej listy wstawimy tylko obiekty mogące być bezpiecznie zrzutowane na Aaa<Object>
(w sensie kompilator statycznie ma stwierdzić, że to jest OK), a do drugiej możemy wstawić jednocześnie Aaa<Integer>
, Aaa<String>
itd
Ktoś ma wątpliwości co ma być z tym pytajnikiem
?
Niech zapisze sobie w rozwiniętej wersji
<? extends Object>
i wtedy powinno być jasne.
KISS, DRY, SOLID, YAGNI itd