CodeGym /Kurslar /JAVA 25 SELF /Generics wildcards

Generics wildcards

JAVA 25 SELF
Səviyyə , Dərs
Mövcuddur

1. Generics-in invariantlığı: List<Number> ≠ List<Integer>

Java-da generics sərtdir: onlar invariantdır. Bu o deməkdir ki, hətta bir tip digərinin alt tipi olsa belə (məsələn, IntegerNumber-ın alt tipidir), bu tiplərlə olan kolleksiyalar bir-biri ilə əlaqəli deyil.

Təsəvvür edin: sizdə almalar üçün qutu var (List<Integer>), kimsə isə deyir ki, bu ümumiyyətlə “meyvə qutusu”dur (List<Number>). Məntiqə uyğundur, axı alma meyvədir. Amma o zaman bu qutuya banan (Double) da qoymaq mümkün olacaq və hər şey pozulacaq — axı qutu əslində “alma” üçündür.

Kod nümunəsi:

List<Integer> intList = new ArrayList<>();
List<Number> numList = intList; // Kompilyasiya xətası!

Kompilyator burada qəsdən sərtdir: o, “alma” siyahısını “meyvələr” siyahısına çevirməyə icazə vermir. Əks halda istənilən şeyi ora əlavə etmək mümkün olardı və proqram icra zamanı yıxılardı.

Ona görə də List<Number>List<Integer> — iki tamamilə fərqli tipdir, baxmayaraq ki, Integer özü Number-ın alt tipidir.

Massivlərdən fərqli olaraq:

Integer[] intArr = new Integer[10];
Number[] numArr = intArr; // İcazəlidir (massivlər kovariantdır)
numArr[0] = 3.14; // İcra zamanı xəta (ArrayStoreException)

Nəticə: generics invariantdır və bu, tiplərin təhlükəsizliyini kompilyasiya mərhələsində təmin etmək üçündür.

2. Tip parametrlərinin sərhədləri

Bəzən generic sinifin və ya metodun hansı tiplərlə işləyə biləcəyini məhdudlaşdırmaq lazımdır. Bunun üçün sərhədlər (bounds) istifadə olunur.

Yuxarı sərhəd (extends)

class Stats<T extends Number> {
    private T[] nums;
    // ...
}

İndi Stats yalnız Number-dən miras alan tiplərlə (Integer, Double, Float və s.) işləyə bilər. Stats<String> yaratmağa cəhd etmək kompilyasiya xətası verəcək.

İnterfeys ilə məhdudiyyət

class Sorter<T extends Comparable<T>> {
    void sort(List<T> list) { /* ... */ }
}

İndi Sorter yalnız Comparable interfeysini reallaşdıran tiplərlə işləyə bilər.

Çoxsaylı sərhədlər

Bir neçə sərhədi eyni anda & vasitəsilə göstərmək olar:

class MyClass<T extends Number & Comparable<T>> { /* ... */ }

T həm Number-dən miras almalı, həm də Comparable<T>-i reallaşdırmalıdır.

Sıra vacibdir: əvvəl sinif, sonra interfeyslər.

3. Wildcard ilə tanışlıq: ?

Wildcard — “əvəzedici” tipdir və belə deyir: “Burada hansısa tip ola bilər, amma hansının olduğunu bilmirəm”.

Nümunələr:

  • List<?> — nə olursa-olsun siyahısı.
  • List<? extends Number>Number-dən miras alan istənilən tipin siyahısı (məsələn, Integer, Double).
  • List<? super Integer>Integer-in istənilən üst tipinin siyahısı (məsələn, Integer, Number, Object).

Wildcards nə üçün lazımdır?

Onlar fərqli, lakin əlaqəli tiplərin kolleksiyaları ilə işləyən metodlar yazmağa imkan verir.

Nümunə: yalnız oxuma (producer)

void printNumbers(List<? extends Number> list) {
    for (Number n : list) {
        System.out.println(n);
    }
    // list.add(123); // Xəta! Elementləri əlavə etmək olmaz
}
  • Elementləri Number kimi oxumaq olar.
  • Əlavə etmək olmaz (yalnız null istisnadır).

Nümunə: yalnız yazma (consumer)

void addIntegers(List<? super Integer> list) {
    list.add(42); // OK
    // Integer x = list.get(0); // Xəta! Hansı tipin qaytarılacağını bilmirik
}
  • Integer tipli (və ya onun alt tipləri) elementləri əlavə etmək olar.
  • Təhlükəsiz şəkildə oxumaq olmaz Integer kimi (yalnız Object kimi).

PECS qaydası

PECS — “Producer Extends, Consumer Super”:

  • Producer — Extends: kolleksiya yalnız məlumat verirsə (producer), ? extends T istifadə edin.
  • Consumer — Super: kolleksiya yalnız məlumat qəbul edirsə (consumer), ? super T istifadə edin.

Yadda saxlamaq asandır: extends — oxumaq üçün (producer), super — yazmaq üçün (consumer).

Müqayisə: massivlər kovariantdır, generics invariantdır

  • Massivlər kovariantdır: Integer[]-i Number[] tipli dəyişənə mənimsətmək olar.
  • Generics invariantdır: List<Integer>-i List<Number> tipli dəyişənə mənimsətmək olmaz.

Wildcards generics-in invariantlığını qismən “yumşaltmağa” imkan verir.

5. Generic-metodlar və tipin çıxarılması; məhdudiyyətlər (type erasure)

Generic-metodlar

İstənilən tiplərlə işləyən generic-metodlar elan etmək olar:

public static <T> void printList(List<T> list) {
    for (T elem : list) {
        System.out.println(elem);
    }
}

Tipin çıxarılması (type inference)

Java parametrin tipini “çıxara” bilər:

List<String> strings = List.of("a", "b");
printList(strings); // T = String

Generics məhdudiyyətləri: tiplərin silinməsi

  • Java-da generics tip silinməsi vasitəsilə reallaşdırılıb: parametr tipləri haqqında məlumat kompilyasiyadan sonra silinir.
  • Bayt-kodda List<String> ilə List<Integer> arasında fərq yoxdur.
  • Generic tiplərin massivlərini yaratmaq olmaz: new List<String>[10] — xəta.
  • Tip parametrləri ilə instanceof istifadə etmək olmaz: obj instanceof List<String> — xəta.

6. Kolleksiyalar və Stream API üzrə praktika

Siyahılar arasında elementlərin kopyalanması

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
    for (T item : src) {
        dest.add(item);
    }
}
  • src — producer (? extends T)
  • dest — consumer (? super T)

İstifadə:

List<Integer> ints = List.of(1, 2, 3);
List<Number> nums = new ArrayList<>();
copy(nums, ints); // OK: Number — Integer-in üst tipidir

Stream API və wildcards

List<Integer> ints = List.of(1, 2, 3);
List<? extends Number> numbers = ints;

numbers.stream()
    .map(Number::doubleValue)
    .forEach(System.out::println);

Wildcard ilə filtrasiya

public static void printAll(List<?> list) {
    for (Object o : list) {
        System.out.println(o);
    }
}

7. Tez-tez edilən səhvlər

Xəta №1: Raw types (xam tiplər).
Xam tiplərdən istifadə tip yoxlamasını söndürür və icra zamanı xətalara aparır.

List list = new ArrayList(); // raw type — pisdir!
list.add("sətir");
list.add(123); // İstənilən şeyi əlavə etmək olar

String s = (String) list.get(1); // ClassCastException!

Heç vaxt raw types istifadə etməyin. Həmişə tip parametrlərini göstərin: List<String>, List<Integer>.

Xəta №2: extends ilə təhlükəsiz olmayan çevirmələr.
? extends ... olan kolleksiyaya element əlavə etməyə cəhd kompilyasiya xətasına səbəb olacaq.

List<? extends Number> nums = new ArrayList<Integer>();
nums.add(3.14); // Kompilyasiya xətası!

Xəta №3: Aşırı yükləmədə tipin “silinməsi”.
Metodları yalnız generic parametrlərinə görə aşırı yükləmək olmaz — tiplər silindikdən sonra imzalar eyni olur.

public void process(List<String> list) { /* ... */ }
public void process(List<Integer> list) { /* ... */ } // Kompilyasiya xətası!

Xəta №4: Generic tiplərin massivləri.
Tip silinməsinə görə parametrizə olunmuş tiplərin massivlərini yaratmaq olmaz.

List<String>[] arr = new List<String>[10]; // Kompilyasiya xətası!
1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Kolleksiya interfeysləri
Kolleksiya interfeysləri
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION