0Pricing
Java Academy · บทเรียน

ไวลด์การ์ดขอบเขตบน

? 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ไวลด์การ์ดขอบเขตบน
  2. ไวลด์การ์ดขอบเขตล่าง
  3. หลักการ PECS
  4. ไวลด์การ์ดใน API
← กลับไปที่ Java Academy