Symbole wieloznaczne z górnym ograniczeniem
? extends T dla producentów
Symbole wieloznaczne z górnym ograniczeniem to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.
Generyki są niezmienne
W języku Java generyki są niezmienne. List<Integer> nie jest typem List<Number>, mimo że Integer jest typem Number.
Może to zaskakiwać początkujących, ale zapewnia spójność systemu typów. Symbole wieloznaczne bezpiecznie łagodzą to ograniczenie.
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> ints = List.of(1, 2, 3);
// List<Number> nums = ints; // would NOT compile
System.out.println("List<Integer> is not List<Number>");
System.out.println(ints);
}
}Wildcard z górnym ograniczeniem
? extends T to wildcard z górnym ograniczeniem. Oznacza „jakiś nieznany typ, który jest typem T lub jego podtypem”.
List<? extends Number> może oznaczać List<Integer>, List<Double> albo List<Number>.
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? extends Number> a = List.of(1, 2, 3);
List<? extends Number> b = List.of(1.5, 2.5);
System.out.println(a.get(0));
System.out.println(b.get(0));
}
}Odczyt z producenta
Za pomocą ? extends Number można bezpiecznie odczytywać elementy jako Number.
Niezależnie od rzeczywistego typu elementów wiadomo, że jest on co najmniej typem Number, więc odczyt jest bezpieczny typowo.
import java.util.List;
public class Main {
static double sum(List<? extends Number> list) {
double total = 0;
for (Number n : list) total += n.doubleValue();
return total;
}
public static void main(String[] args) {
System.out.println(sum(List.of(1, 2, 3)));
System.out.println(sum(List.of(1.5, 2.5)));
}
}Nie można do niej dodawać elementów
Oto haczyk: do listy ? extends Number nie można dodawać elementów (z wyjątkiem null).
Kompilator nie zna dokładnego typu elementów. Może to być List<Integer>, więc dodanie Double byłoby niebezpieczne.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? extends Number> list = new ArrayList<Integer>();
// list.add(1); // does NOT compile
// list.add(1.5); // does NOT compile
System.out.println("Cannot add to ? extends Number");
System.out.println("size = " + list.size());
}
}Dlaczego zapisywanie jest zabronione
Wyobraźmy sobie, że byłoby to dozwolone. Można by przekazać List<Integer> jako List<? extends Number>, a następnie dodać Double.
Późniejszy kod odczytujący listę jako List<Integer> otrzymałby wyjątek ClassCastException. Kompilator zapobiega temu, zabraniając zapisu.
import java.util.List;
public class Main {
public static void main(String[] args) {
// Conceptual: this is why add is blocked
System.out.println("Writing a Double into a List<Integer> would corrupt it");
System.out.println("so ? extends bans all adds");
}
}Przypadek użycia producenta
Należy używać ? extends T, gdy metoda tylko produkuje dla Państwa wartości, czyli je odczytuje.
Dobrym przykładem jest metoda kopiująca elementy ze źródłowej kolekcji.
import java.util.ArrayList;
import java.util.List;
public class Main {
static List<Number> copyOut(List<? extends Number> source) {
List<Number> dest = new ArrayList<>();
for (Number n : source) dest.add(n);
return dest;
}
public static void main(String[] args) {
System.out.println(copyOut(List.of(1, 2, 3)));
}
}Parametry typów z ograniczeniami a wildcardy
Nie należy mylić <T extends Number> (ograniczonego parametru typu) z ? extends Number (wildcardem).
- Parametr typu: nadaje typowi nazwę, której można używać w całej metodzie.
- Wildcard: anonimowy, nieznany podtyp używany wtedy, gdy nie trzeba nadawać mu nazwy.
import java.util.List;
public class Main {
static <T extends Number> T first(List<T> list) { return list.get(0); }
public static void main(String[] args) {
Integer i = first(List.of(10, 20));
System.out.println(i);
}
}Wiele ograniczeń
Parametr typu może mieć wiele ograniczeń połączonych za pomocą &, na przykład <T extends Number & Comparable<T>>.
Wildcardy obsługują tylko jedno górne ograniczenie, dlatego wiele ograniczeń wymaga nazwanego parametru typu.
import java.util.List;
public class Main {
static <T extends Number & Comparable<T>> T max(List<T> list) {
T best = list.get(0);
for (T t : list) if (t.compareTo(best) > 0) best = t;
return best;
}
public static void main(String[] args) {
System.out.println(max(List.of(3, 9, 1, 7)));
}
}Wildcardy z Collections.max
JDK szeroko wykorzystuje górne ograniczenia, na przykład podczas sumowania lub przeszukiwania kolekcji tylko do odczytu.
W tym przykładzie znajdujemy największy element listy określonej jako producent.
import java.util.List;
public class Main {
static double largest(List<? extends Number> nums) {
double max = Double.NEGATIVE_INFINITY;
for (Number n : nums) max = Math.max(max, n.doubleValue());
return max;
}
public static void main(String[] args) {
System.out.println(largest(List.of(4, 2, 9, 1)));
System.out.println(largest(List.of(0.5, 9.9, 3.3)));
}
}null to jedyna wartość, którą można zapisać
Jedyną wartością, którą można dodać do kolekcji ? extends T, jest null, ponieważ null można przypisać do dowolnego typu referencyjnego.
Rzadko jest to przydatne, ale wyjaśnia, dlaczego metoda add nie jest całkowicie zabroniona, a jedynie praktycznie niedostępna.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? extends Number> list = new ArrayList<Number>();
// Only null is assignable; demonstrated conceptually
System.out.println("Only null could be added; we avoid that");
System.out.println("size = " + list.size());
}
}Odczyt jako Object zawsze działa
Ponieważ każdy typ rozszerza Object, elementy dowolnej listy z wildcardem można zawsze odczytywać jako Object.
W przypadku górnego ograniczenia Number dodatkowo można odczytywać je bezpośrednio jako Number.
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? extends Number> nums = List.of(1, 2.0, 3L);
for (Object o : nums) System.out.println(o.getClass().getSimpleName());
}
}Szybkie sprawdzenie
Proszę sprawdzić, jak dobrze rozumieją Państwo górne ograniczenia.
Podsumowanie
Poznali Państwo wildcardy z górnym ograniczeniem:
? extends Toznacza nieznany podtyp typu T.- Elementy można odczytywać jako T — kolekcja jest producentem.
- Nie można dodawać elementów innych niż
null. - Typy generyczne są niezmiennicze, a wildcardy bezpiecznie łagodzą to ograniczenie.
Następnie poznamy ich lustrzane odbicie: wildcardy z dolnym ograniczeniem.
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? extends Number> nums = List.of(1, 2, 3);
System.out.println("Upper bound recap: read " + nums.get(0));
}
}Często zadawane pytania
Czy lekcja „Symbole wieloznaczne z górnym ograniczeniem” jest bezpłatna?
Tak — pełny tekst „Symbole wieloznaczne z górnym ograniczeniem” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Symbole wieloznaczne z górnym ograniczeniem”?
? extends T dla producentów Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Java Academy?
Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Symbole wieloznaczne z górnym ograniczeniem”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?
Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Symbole wieloznaczne z górnym ograniczeniem
- Symbole wieloznaczne z dolnym ograniczeniem
- Zasada PECS
- Symbole wieloznaczne w API