Java Academy · Pelajaran

Prinsip PECS

Producer Extends, Consumer Super

Pelajaran 3 daripada 413 langkah

Prinsip PECS ialah pelajaran Java Academy percuma di CoddyKit. Ini ialah pelajaran 3 daripada 4. Anda boleh membaca keseluruhan pelajaran di bawah secara percuma — kemudian berlatih secara praktikal dalam pelayar menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran Java Academy, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus Java Academy merangkumi sejumlah 4 pelajaran.

Maksud PECS

PECS bermaksud Pengeluar menggunakan sempadan atas, Pengguna menggunakan sempadan bawah. Ia ialah mnemonik daripada buku Effective Java karya Joshua Bloch.

  • Jika parameter menghasilkan T (anda membacanya), gunakan ? extends T.
  • Jika parameter menggunakan T (anda menulis kepadanya), gunakan ? super T.
public class Main {
    public static void main(String[] args) {
        System.out.println("Producer Extends, Consumer Super");
    }
}

Kaedah copy Kanonik

Contoh PECS klasik ialah kaedah copy. Sumber menghasilkan, jadi ia ialah ? extends T. Destinasi menggunakan, jadi ia ialah ? super T.

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

public class Main {
    static <T> void copy(List<? extends T> src, List<? super T> dest) {
        for (T t : src) dest.add(t);
    }
    public static void main(String[] args) {
        List<Integer> src = List.of(1, 2, 3);
        List<Object> dest = new ArrayList<>();
        copy(src, dest);
        System.out.println(dest);
    }
}

Sebab Kedua-dua Kad Bebas Menambah Fleksibiliti

Terima kasih kepada PECS, copy berfungsi untuk banyak gabungan jenis: sumber integer ke dalam destinasi nombor atau objek, dan sumber String ke dalam destinasi CharSequence.

Tanpa kad bebas, jenis-jenis itu perlu sepadan dengan tepat, lalu mengehadkan penggunaan semula.

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

public class Main {
    static <T> void copy(List<? extends T> src, List<? super T> dest) {
        for (T t : src) dest.add(t);
    }
    public static void main(String[] args) {
        List<Integer> ints = List.of(10, 20);
        List<Number> nums = new ArrayList<>();
        copy(ints, nums);
        System.out.println(nums);
    }
}

Contoh Pengeluar

Kaedah yang hanya membaca daripada koleksinya patut menggunakan sempadan atas. Di sini kita menjumlahkan pengeluar.

import java.util.List;

public class Main {
    static double sum(List<? extends Number> producer) {
        double total = 0;
        for (Number n : producer) 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.1, 2.2)));
    }
}

Contoh Pengguna

Kaedah yang hanya menulis kepada koleksinya patut menggunakan sempadan bawah. Di sini kita menolak nilai ke dalam pengguna.

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

public class Main {
    static void pushDigits(List<? super Integer> consumer) {
        for (int i = 0; i < 5; i++) consumer.add(i);
    }
    public static void main(String[] args) {
        List<Number> nums = new ArrayList<>();
        pushDigits(nums);
        System.out.println(nums);
    }
}

Kedua-dua Peranan: Gunakan Jenis Biasa

Jika parameter berperanan sebagai pengeluar dan pengguna, parameter itu tidak patut menggunakan kad bebas.

Kaedah yang membaca dan menulis senarai yang sama memerlukan jenis tepat T.

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

public class Main {
    static <T> void rotate(List<T> list) {
        if (list.isEmpty()) return;
        T last = list.remove(list.size() - 1);
        list.add(0, last);
    }
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>(List.of(1, 2, 3));
        rotate(list);
        System.out.println(list);
    }
}

PECS dalam Comparator

JDK menggunakan PECS secara meluas. Collections.max menerima Comparator<? super T> kerana pembanding menggunakan nilai T untuk membandingkannya.

import java.util.Comparator;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        Comparator<Object> byHash = Comparator.comparingInt(Object::hashCode);
        List<String> words = List.of("a", "bb", "ccc");
        String max = java.util.Collections.max(words, byHash);
        System.out.println(max);
    }
}

PECS dalam Stream.collect

Banyak antara muka fungsi mengikuti PECS. Consumer<? super T> boleh menerima pengendali yang ditulis untuk jenis yang lebih luas.

import java.util.List;
import java.util.function.Consumer;

public class Main {
    public static void main(String[] args) {
        Consumer<Object> printer = o -> System.out.println("got: " + o);
        List<String> items = List.of("x", "y");
        items.forEach(printer); // forEach takes Consumer<? super T>
    }
}

Pengeluar dan Pengguna Fungsi

Function<? super T, ? extends R> ialah bentuk PECS lengkap: input digunakan (super), manakala output dihasilkan (extends).

Ini membolehkan penukaran menerima input yang lebih luas dan mengembalikan output yang lebih khusus.

import java.util.function.Function;

public class Main {
    static <T, R> R apply(Function<? super T, ? extends R> fn, T value) {
        return fn.apply(value);
    }
    public static void main(String[] args) {
        Function<Object, Integer> len = o -> o.toString().length();
        System.out.println(apply(len, "hello"));
    }
}

Jenis Pulangan: Elakkan Kad Bebas

Jangan gunakan kad bebas dalam jenis pulangan. Kad bebas memaksa pemanggil turut mengendalikannya, lalu mendedahkan kerumitan.

Kembalikan jenis konkrit dan kekalkan kad bebas pada parameter apabila kad bebas itu membantu fleksibiliti.

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

public class Main {
    // Good: concrete return type
    static List<Number> doubled(List<? extends Number> src) {
        List<Number> out = new ArrayList<>();
        for (Number n : src) out.add(n.doubleValue() * 2);
        return out;
    }
    public static void main(String[] args) {
        System.out.println(doubled(List.of(1, 2, 3)));
    }
}

Gabungan PECS Lengkap

Menggabungkan semuanya: operasi merge yang membaca daripada dua pengeluar ke dalam seorang pengguna.

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

public class Main {
    static <T> void merge(List<? extends T> a, List<? extends T> b, List<? super T> out) {
        for (T t : a) out.add(t);
        for (T t : b) out.add(t);
    }
    public static void main(String[] args) {
        List<Integer> a = List.of(1, 2);
        List<Integer> b = List.of(3, 4);
        List<Number> out = new ArrayList<>();
        merge(a, b, out);
        System.out.println(out);
    }
}

Semakan Pantas

Gunakan peraturan PECS.

Rumusan

Anda telah mempelajari prinsip PECS:

  • Pengeluar dengan Sempadan Atas: parameter baca sahaja menggunakan ? extends T.
  • Pengguna dengan Sempadan Bawah: parameter tulis sahaja menggunakan ? super T.
  • Parameter yang mempunyai kedua-dua peranan menggunakan parameter jenis biasa.
  • Elakkan kad bebas dalam jenis pulangan.

Seterusnya, gunakan idea ini dalam reka bentuk API sebenar.

public class Main {
    public static void main(String[] args) {
        System.out.println("PECS recap: Producer Extends, Consumer Super");
    }
}
Percuma untuk bermula

Pelajari Java dengan tutor kecerdasan buatan — percuma

Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.

Kursus
104
Pelajaran
374

Soalan Lazim

Adakah pelajaran “Prinsip PECS” percuma?

Ya — teks penuh “Prinsip PECS” boleh dibaca secara percuma di web ini. Untuk berlatih secara interaktif menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7, serta membuka kunci baki kursus Java Academy, tingkat taraf kepada CoddyKit PRO. Kursus Java Academy merangkumi sejumlah 4 pelajaran.

Apakah yang akan saya pelajari dalam “Prinsip PECS”?

Producer Extends, Consumer Super Anda berlatih Java Academy menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.

Adakah saya memerlukan pengalaman untuk memulakan Java Academy?

Tiada pengalaman terdahulu diperlukan. Pembelajaran Java Academy di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 3 daripada 4.

Berapa lamakah pelajaran “Prinsip PECS” diambil?

Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.

Bolehkah saya menulis dan menjalankan kod dalam pelajaran Java Academy ini?

Ya. Setiap pelajaran Java Academy menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.

Semua pelajaran dalam kursus ini

  1. Kad Liar Berhad Atas
  2. Kad Liar Berhad Bawah
  3. Prinsip PECS
  4. Kad Liar dalam API
← Kembali ke Java Academy