ไวลด์การ์ดขอบเขตบน
? extends T สำหรับผู้ผลิตข้อมูล
ไวลด์การ์ดขอบเขตบน เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
เจเนอริกส์มีความไม่แปรผัน
ใน Java เจเนอริกส์มีความไม่แปรผัน ชนิด List<Integer> ไม่ใช่ชนิด List<Number> แม้ว่าชนิดจำนวนเต็มจะเป็นชนิดย่อยของชนิดจำนวนก็ตาม
เรื่องนี้อาจทำให้ผู้เริ่มต้นแปลกใจ แต่ช่วยให้ระบบชนิดข้อมูลมีความถูกต้อง ไวลด์การ์ดช่วยผ่อนคลายข้อจำกัดนี้อย่างปลอดภัย
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);
}
}ไวลด์การ์ดแบบมีขอบเขตบน
? extends T คือไวลด์การ์ดแบบมีขอบเขตบน หมายถึง "ชนิดที่ไม่ทราบแน่ชัดชนิดหนึ่ง ซึ่งเป็น T หรือชนิดย่อยของ T"
List<? extends Number> สามารถอ้างถึง List<Integer>, List<Double> หรือ 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));
}
}การอ่านจากแหล่งผลิต
เมื่อใช้ ? extends Number คุณสามารถอ่านองค์ประกอบในรูปแบบ Number ได้อย่างปลอดภัย
ไม่ว่าชนิดขององค์ประกอบจริงจะเป็นอะไร ก็รับประกันได้ว่าเป็นชนิดตัวเลขอย่างน้อย ดังนั้นการอ่านจึงปลอดภัยในด้านชนิดข้อมูล
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)));
}
}ไม่สามารถเขียนลงในรายการได้
ประเด็นสำคัญคือ คุณไม่สามารถใช้ add เพื่อเพิ่มองค์ประกอบลงในรายการ ? extends Number ได้ (ยกเว้นค่าว่าง)
คอมไพเลอร์ไม่ทราบชนิดข้อมูลขององค์ประกอบที่แน่นอน รายการนั้นอาจเป็น List<Integer> ดังนั้นการเพิ่มค่าทศนิยมจึงไม่ปลอดภัย
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());
}
}เหตุใดจึงห้ามเขียน
ลองนึกภาพว่าหากอนุญาตให้ทำได้ คุณอาจส่ง List<Integer> เป็น List<? extends Number> แล้วเพิ่มค่าทศนิยมเข้าไป
โค้ดที่อ่านรายการนี้ในภายหลังโดยมองว่าเป็น List<Integer> จะเกิด ClassCastException คอมไพเลอร์จึงป้องกันปัญหานี้ด้วยการห้ามเขียน
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");
}
}กรณีใช้งานสำหรับผู้ผลิต
ใช้ ? extends T เมื่อเมธอดทำหน้าที่สร้างค่าให้คุณเพียงอย่างเดียว (อ่านค่าออกมา)
เมธอดที่คัดลอกองค์ประกอบออกจากชุดข้อมูลต้นทางเป็นตัวอย่างที่เหมาะสมอย่างยิ่ง
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)));
}
}พารามิเตอร์ชนิดข้อมูลแบบมีขอบเขตเทียบกับไวลด์การ์ด
อย่าสับสนระหว่าง <T extends Number> (ซึ่งเป็นพารามิเตอร์ชนิดข้อมูลแบบมีขอบเขต) กับ ? extends Number (ซึ่งเป็นไวลด์การ์ด)
- พารามิเตอร์ชนิดข้อมูล: ตั้งชื่อชนิดข้อมูลเพื่อใช้งานตลอดทั้งเมธอด
- ไวลด์การ์ด: ชนิดย่อยที่ไม่ทราบแน่ชัดและไม่มีชื่อ ใช้เมื่อไม่จำเป็นต้องตั้งชื่อชนิดนั้น
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);
}
}หลายขอบเขต
พารามิเตอร์ชนิดข้อมูลสามารถมีหลายขอบเขตโดยใช้ & เช่น <T extends Number & Comparable<T>>
ไวลด์การ์ดรองรับขอบเขตบนได้เพียงหนึ่งขอบเขต ดังนั้นหากต้องการหลายขอบเขตจึงต้องใช้พารามิเตอร์ชนิดข้อมูลที่มีชื่อ
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)));
}
}ไวลด์การ์ดกับ Collections.max
JDK ใช้ขอบเขตบนอย่างแพร่หลาย ตัวอย่างเช่น การหาผลรวมหรือการสำรวจชุดข้อมูลแบบอ่านอย่างเดียว
ในที่นี้ เราจะหาค่าสูงสุดของรายการที่มีชนิดข้อมูลแบบผู้ผลิต
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)));
}
}ค่าว่างเป็นค่าเดียวที่เขียนได้
สิ่งเดียวที่คุณสามารถเพิ่มลงในชุดข้อมูล ? extends T ได้คือ null เพราะค่าว่างสามารถกำหนดให้กับชนิดข้อมูลอ้างอิงใด ๆ ได้
กรณีนี้ไม่ค่อยมีประโยชน์ แต่ช่วยอธิบายว่า add ไม่ได้ถูกห้ามโดยสิ้นเชิง เพียงแต่แทบไม่สามารถใช้งานได้จริง
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());
}
}การอ่านออกมาเป็น Object ทำได้เสมอ
เนื่องจากทุกชนิดข้อมูลเป็นชนิดย่อยของ Object คุณจึงสามารถอ่านองค์ประกอบจากรายการไวลด์การ์ดใด ๆ เป็น Object ได้เสมอ
เมื่อกำหนดขอบเขตบนเป็นชนิดตัวเลข คุณจะได้ประโยชน์เพิ่มเติมคือสามารถอ่านค่าเป็นชนิดตัวเลขได้โดยตรง
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());
}
}ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจของคุณเกี่ยวกับขอบเขตบน
ทบทวน
คุณได้เรียนรู้เกี่ยวกับไวลด์การ์ดที่มีขอบเขตบนแล้ว:
? extends Tหมายถึงชนิดย่อยที่ไม่ทราบแน่ชัดของ T- คุณสามารถอ่านองค์ประกอบเป็น T ได้ (ผู้ผลิต)
- คุณไม่สามารถเพิ่มองค์ประกอบที่ไม่ใช่ค่าว่างได้
- ชนิดข้อมูลทั่วไปมีความไม่แปรผัน และไวลด์การ์ดช่วยผ่อนคลายข้อจำกัดนี้อย่างปลอดภัย
ถัดไปคือภาพสะท้อนกันของแนวคิดนี้: ไวลด์การ์ดที่มีขอบเขตล่าง
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));
}
}คำถามที่พบบ่อย
บทเรียน “ไวลด์การ์ดขอบเขตบน” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ไวลด์การ์ดขอบเขตบน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ไวลด์การ์ดขอบเขตบน”
? extends T สำหรับผู้ผลิตข้อมูล คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “ไวลด์การ์ดขอบเขตบน” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม
ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- ไวลด์การ์ดขอบเขตบน
- ไวลด์การ์ดขอบเขตล่าง
- หลักการ PECS
- ไวลด์การ์ดใน API