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

ไวลด์การ์ดใน API

ออกแบบเมธอด泛型ที่ยืดหยุ่น

ไวลด์การ์ดใน API เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

การออกแบบเมธอดที่ยืดหยุ่น

ไวลด์การ์ดเป็นเครื่องมือหลักสำหรับผู้ออกแบบส่วนติดต่อโปรแกรม ไวลด์การ์ดที่วางไว้อย่างเหมาะสมช่วยให้ผู้เรียกส่งชนิดข้อมูลได้มากขึ้นโดยไม่ต้องแปลงชนิด

เป้าหมายคือให้ผู้เรียกมีความยืดหยุ่นสูงสุด ขณะที่ภายในยังปลอดภัยด้านชนิดข้อมูลอย่างสมบูรณ์

import java.util.List;

public class Main {
    static double total(List<? extends Number> items) {
        double sum = 0;
        for (Number n : items) sum += n.doubleValue();
        return sum;
    }
    public static void main(String[] args) {
        System.out.println(total(List.of(1, 2, 3)));
        System.out.println(total(List.of(1.5, 2.5)));
    }
}

ไวลด์การ์ดแบบไม่มีขอบเขต

List<?> คือไวลด์การ์ดแบบไม่มีขอบเขต หมายถึงรายการของชนิดข้อมูลบางชนิดที่ไม่ทราบแน่ชัด

ใช้เมื่อเมธอดไม่สนใจชนิดข้อมูลขององค์ประกอบ เช่น การนับองค์ประกอบหรือการตรวจสอบว่ารายการว่างหรือไม่

import java.util.List;

public class Main {
    static int size(List<?> any) {
        return any.size();
    }
    public static void main(String[] args) {
        System.out.println(size(List.of("a", "b")));
        System.out.println(size(List.of(1, 2, 3)));
    }
}

List<?> เทียบกับ List<Object>

สองอย่างนี้แตกต่างกัน List<Object> รับเฉพาะรายการที่ประกาศเป็น Object ส่วน List<?> รับรายการของชนิดข้อมูลใดก็ได้

คุณสามารถส่ง List<String> ไปยังพารามิเตอร์ List<?> ได้ แต่ส่งไปยังพารามิเตอร์ List<Object> ไม่ได้

import java.util.List;

public class Main {
    static void printAll(List<?> list) {
        for (Object o : list) System.out.println(o);
    }
    public static void main(String[] args) {
        List<String> names = List.of("Ada", "Linus");
        printAll(names); // works; List<Object> would reject this
    }
}

เมื่อพารามิเตอร์ชนิดข้อมูลชัดเจนกว่า

หากคุณต้องอ้างอิงถึงชนิดข้อมูลขององค์ประกอบมากกว่าหนึ่งครั้ง การใช้พารามิเตอร์ชนิดข้อมูลที่มีชื่อจะอ่านเข้าใจได้ดีกว่าไวลด์การ์ด

ลองเปรียบเทียบ void swap(List<?> l, int i, int j) (ซึ่งต้องใช้ตัวช่วยส่วนตัว) กับรุ่นทั่วไปที่เขียนได้สะอาดกว่า

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

public class Main {
    static <T> void swap(List<T> list, int i, int j) {
        T tmp = list.get(i);
        list.set(i, list.get(j));
        list.set(j, tmp);
    }
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(List.of("a", "b", "c"));
        swap(list, 0, 2);
        System.out.println(list);
    }
}

เทคนิคการจับไวลด์การ์ดด้วยตัวช่วย

หากส่วนติดต่อโปรแกรมสาธารณะจำเป็นต้องใช้ List<?> คุณสามารถมอบหมายงานให้ตัวช่วยทั่วไปแบบส่วนตัวเพื่อจับไวลด์การ์ดได้

ตัวช่วยจะตั้งชื่อชนิดข้อมูลเป็น T เพื่อให้สามารถเขียนค่ากลับลงในรายการได้

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

public class Main {
    public static void reverse(List<?> list) { reverseHelper(list); }
    private static <T> void reverseHelper(List<T> list) {
        for (int i = 0, j = list.size() - 1; i < j; i++, j--) {
            T tmp = list.get(i);
            list.set(i, list.get(j));
            list.set(j, tmp);
        }
    }
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>(List.of(1, 2, 3, 4));
        reverse(list);
        System.out.println(list);
    }
}

พารามิเตอร์ผู้ผลิตในส่วนติดต่อโปรแกรมจริง

การออกแบบ addAll ที่ดึงข้อมูลจากแหล่งข้อมูลที่เข้ากันได้ทุกชนิดจะใช้ขอบเขตบนกับแหล่งข้อมูล

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

public class Main {
    static <T> void addAll(List<T> target, Collection<? extends T> source) {
        for (T t : source) target.add(t);
    }
    public static void main(String[] args) {
        List<Number> nums = new ArrayList<>();
        addAll(nums, List.of(1, 2, 3));
        System.out.println(nums);
    }
}

พารามิเตอร์ผู้บริโภคในส่วนติดต่อโปรแกรมจริง

ส่วนติดต่อโปรแกรมสำหรับรับข้อมูลที่เขียนผลลัพธ์จะใช้ขอบเขตล่างกับปลายทาง เพื่อให้ผู้เรียกส่งภาชนะที่รองรับชนิดข้อมูลกว้างกว่าได้

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

public class Main {
    static <T> void repeat(T value, int times, List<? super T> sink) {
        for (int i = 0; i < times; i++) sink.add(value);
    }
    public static void main(String[] args) {
        List<Object> out = new ArrayList<>();
        repeat("hi", 3, out);
        System.out.println(out);
    }
}

ไวลด์การ์ดกับชนิดข้อมูลทั่วไปซ้อนกัน

ชนิดข้อมูลทั่วไปที่ซ้อนกันมักต้องใช้ไวลด์การ์ด เมธอดที่รับรายการซึ่งแต่ละองค์ประกอบเป็นรายการชนิดใดก็ได้จะใช้ List<? extends List<?>>

import java.util.List;

public class Main {
    static int countAll(List<? extends List<?>> lists) {
        int total = 0;
        for (List<?> inner : lists) total += inner.size();
        return total;
    }
    public static void main(String[] args) {
        System.out.println(countAll(List.of(List.of(1, 2), List.of("a", "b", "c"))));
    }
}

หลีกเลี่ยงการใช้ชนิดข้อมูลทั่วไปเกินจำเป็น

ไม่ใช่ทุกเมธอดที่ต้องใช้ไวลด์การ์ด หากผู้เรียกส่งชนิดข้อมูลเพียงชนิดเดียวเสมอ การใช้พารามิเตอร์ธรรมดาจะชัดเจนกว่า

เพิ่มไวลด์การ์ดเมื่อพบว่าผู้เรียกจริงถูกขัดขวางด้วยความไม่แปรผัน ไม่ใช่เพิ่มไว้ล่วงหน้า

import java.util.List;

public class Main {
    // Simple and clear; no wildcard needed for this internal use
    static String joinStrings(List<String> parts) {
        return String.join(", ", parts);
    }
    public static void main(String[] args) {
        System.out.println(joinStrings(List.of("a", "b", "c")));
    }
}

การอธิบายเจตนาของไวลด์การ์ด

ไวลด์การ์ดสื่อถึงเจตนา ? extends T บอกผู้อ่านว่า “ฉันอ่านค่านี้เท่านั้น” ส่วน ? super T บอกว่า “ฉันเขียนค่านี้เท่านั้น”

การเลือกไวลด์การ์ดที่เหมาะสมเป็นรูปแบบหนึ่งของโค้ดที่อธิบายตัวเอง และยังบังคับใช้สัญญาการทำงานด้วย

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

public class Main {
    // src: read-only producer; dest: write-only consumer
    static <T> void transfer(List<? extends T> src, List<? super T> dest) {
        for (T t : src) dest.add(t);
    }
    public static void main(String[] args) {
        List<Number> dest = new ArrayList<>();
        transfer(List.of(1, 2), dest);
        System.out.println(dest);
    }
}

ส่วนติดต่อโปรแกรม Stack แบบทั่วไป

ตัวอย่าง Stack อันโด่งดังของ Bloch: pushAll รับข้อมูลจากผู้ผลิต (ขอบเขตบน) ส่วน popAll สร้างข้อมูลให้ผู้บริโภค (ขอบเขตล่าง)

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.ArrayList;
import java.util.List;

public class Main {
    static class Stack<E> {
        private final Deque<E> items = new ArrayDeque<>();
        void pushAll(Collection<? extends E> src) { for (E e : src) items.push(e); }
        void popAll(Collection<? super E> dst) { while (!items.isEmpty()) dst.add(items.pop()); }
    }
    public static void main(String[] args) {
        Stack<Integer> s = new Stack<>();
        s.pushAll(List.of(1, 2, 3));
        List<Number> out = new ArrayList<>();
        s.popAll(out);
        System.out.println(out);
    }
}

ตรวจสอบความเข้าใจอย่างรวดเร็ว

ทดสอบสัญชาตญาณของคุณในการออกแบบส่วนติดต่อโปรแกรม

ทบทวน

คุณได้เรียนรู้การออกแบบส่วนติดต่อโปรแกรมด้วยไวลด์การ์ดแล้ว:

  • List<?> สำหรับเมธอดแบบอ่านอย่างเดียวที่ไม่ขึ้นกับชนิดข้อมูล
  • ใช้ PECS กับพารามิเตอร์เพื่อเพิ่มความยืดหยุ่น
  • ใช้เทคนิคจับไวลด์การ์ดด้วยตัวช่วยส่วนตัวเมื่อจำเป็นต้องเขียนผ่านไวลด์การ์ด
  • หลีกเลี่ยงไวลด์การ์ดในชนิดผลลัพธ์และอย่าใช้ชนิดข้อมูลทั่วไปเกินจำเป็น

คุณเรียนจบหลักสูตรไวลด์การ์ดและ PECS แล้ว

public class Main {
    public static void main(String[] args) {
        System.out.println("Wildcards in APIs course complete");
    }
}

คำถามที่พบบ่อย

บทเรียน “ไวลด์การ์ดใน API” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ไวลด์การ์ดใน API” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ไวลด์การ์ดใน API”

ออกแบบเมธอด泛型ที่ยืดหยุ่น คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “ไวลด์การ์ดใน API” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม

ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

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

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