หลักการ PECS
ผู้ผลิตใช้ Extends ผู้บริโภคใช้ Super
หลักการ PECS เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
PECS ย่อมาจากอะไร
PECS หมายถึง ผู้ผลิตใช้ขอบเขตบน ผู้บริโภคใช้ขอบเขตล่าง เป็นคำช่วยจำจากหนังสือ Java อย่างมีประสิทธิภาพของ Joshua Bloch
- หากพารามิเตอร์สร้างค่า T (คุณอ่านค่าจากพารามิเตอร์นั้น) ให้ใช้
? extends T - หากพารามิเตอร์รับค่า T (คุณเขียนค่าลงในพารามิเตอร์นั้น) ให้ใช้
? super T
public class Main {
public static void main(String[] args) {
System.out.println("Producer Extends, Consumer Super");
}
}เมธอด copy แบบมาตรฐาน
ตัวอย่าง PECS แบบคลาสสิกคือเมธอด copy โดยต้นทางสร้างค่า จึงใช้ ? extends T ส่วนปลายทางรับค่า จึงใช้ ? 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);
}
}เหตุใดไวลด์การ์ดทั้งสองชนิดจึงเพิ่มความยืดหยุ่น
ด้วย PECS เมธอด copy จึงรองรับการจับคู่ชนิดข้อมูลได้หลากหลาย เช่น แหล่งข้อมูลจำนวนเต็มไปยังปลายทางชนิดตัวเลขหรือออบเจ็กต์ และแหล่งข้อมูล String ไปยังปลายทาง CharSequence
หากไม่มีไวลด์การ์ด ชนิดข้อมูลจะต้องตรงกันทุกประการ ทำให้การนำกลับมาใช้ใหม่ทำได้ยากมาก
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);
}
}ตัวอย่างผู้ผลิต
เมธอดที่อ่านจากชุดข้อมูลเพียงอย่างเดียวควรใช้ขอบเขตบน ในที่นี้เราจะหาผลรวมจากผู้ผลิต
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)));
}
}ตัวอย่างผู้บริโภค
เมธอดที่เขียนลงในชุดข้อมูลเพียงอย่างเดียวควรใช้ขอบเขตล่าง ในที่นี้เราจะใส่ค่าลงในผู้บริโภค
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);
}
}มีทั้งสองบทบาท: ใช้ชนิดข้อมูลธรรมดา
หากพารามิเตอร์เป็นทั้งผู้ผลิตและผู้บริโภค ก็ไม่ควรใช้ไวลด์การ์ด
เมธอดที่อ่านและเขียนรายการเดียวกันจำเป็นต้องใช้ชนิดข้อมูล 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 ในตัวเปรียบเทียบ
JDK ใช้ PECS อย่างแพร่หลาย Collections.max รับค่า Comparator<? super T> เพราะตัวเปรียบเทียบรับค่า T เพื่อใช้เปรียบเทียบ
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 ใน Stream.collect
ส่วนติดต่อโปรแกรมเชิงฟังก์ชันจำนวนมากทำตาม PECS Consumer<? super T> สามารถรับตัวจัดการที่เขียนไว้สำหรับชนิดข้อมูลที่กว้างกว่าได้
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>
}
}ฟังก์ชันในบทบาทผู้ผลิตและผู้บริโภค
Function<? super T, ? extends R> คือรูปแบบ PECS ที่ครบถ้วน: อินพุตถูกนำไปใช้ (ขอบเขตล่าง) และเอาต์พุตถูกสร้างขึ้น (ขอบเขตบน)
รูปแบบนี้ทำให้การแปลงข้อมูลรับอินพุตที่กว้างกว่าและคืนเอาต์พุตที่จำเพาะกว่าได้
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"));
}
}ชนิดผลลัพธ์: หลีกเลี่ยงไวลด์การ์ด
อย่าใช้ไวลด์การ์ดในชนิดผลลัพธ์ เพราะจะบังคับให้ผู้เรียกต้องจัดการกับไวลด์การ์ดด้วย ทำให้ความซับซ้อนรั่วไหลออกไป
ให้คืนชนิดข้อมูลที่เป็นรูปธรรม และเก็บไวลด์การ์ดไว้ที่พารามิเตอร์ซึ่งช่วยเพิ่มความยืดหยุ่น
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)));
}
}การ merge ตาม PECS แบบครบถ้วน
เมื่อนำทุกอย่างมารวมกัน: การ merge ที่อ่านจากผู้ผลิตสองรายแล้วเขียนลงในผู้บริโภครายเดียว
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);
}
}ตรวจสอบความเข้าใจอย่างรวดเร็ว
นำกฎ PECS ไปใช้
ทบทวน
คุณได้เรียนรู้หลักการ PECS แล้ว:
- ผู้ผลิตใช้ขอบเขตบน: พารามิเตอร์แบบอ่านอย่างเดียวใช้
? extends T - ผู้บริโภคใช้ขอบเขตล่าง: พารามิเตอร์แบบเขียนอย่างเดียวใช้
? super T - พารามิเตอร์ที่มีทั้งสองบทบาทให้ใช้พารามิเตอร์ชนิดข้อมูลธรรมดา
- หลีกเลี่ยงไวลด์การ์ดในชนิดผลลัพธ์
ถัดไปคือการนำแนวคิดเหล่านี้ไปใช้ในการออกแบบส่วนติดต่อโปรแกรมจริง
public class Main {
public static void main(String[] args) {
System.out.println("PECS recap: Producer Extends, Consumer Super");
}
}คำถามที่พบบ่อย
บทเรียน “หลักการ PECS” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “หลักการ PECS” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “หลักการ PECS”
ผู้ผลิตใช้ Extends ผู้บริโภคใช้ Super คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “หลักการ PECS” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม
ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ