S SmartDocs
Series: Angelia java 163 lines · Updated 2026-09-27

Demo.java

Angelia/Java/Week_6_7/code/06_generics_collections/Demo.java

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 * 06_generics_collections 示範程式:Object、轉型、泛型、型別擦除、Collections API。
 *
 * 編譯與執行:
 *   javac *.java
 *   java -ea Demo
 */
public class Demo {

    /** 泛型方法:對「任何型別」的 List 都能用;T 由編譯器從參數推論。 */
    static <T> T firstOrDefault(List<T> list, T fallback) {
        if (list.isEmpty()) {
            return fallback;
        }
        return list.get(0);
    }

    /** 有界型別參數:T 必須是 Comparable<T>,才能呼叫 compareTo。 */
    static <T extends Comparable<T>> T max(List<T> list) {
        if (list.isEmpty()) {
            throw new IllegalArgumentException("空串列沒有最大值");
        }
        T best = list.get(0);
        for (T x : list) {
            if (x.compareTo(best) > 0) {
                best = x;
            }
        }
        return best;
    }

    public static void main(String[] args) {
        System.out.println("=== 1. Object:所有類別的共同祖先 ===");
        Object x = "hello";        // String 是 Object 的子型別
        Object y = 42;             // 自動裝箱:int → Integer,Integer 是 Object 的子型別
        System.out.println("  x = " + x + ",y = " + y);
        // x.length();             // 編譯錯誤:編譯器只知道 x 是 Object,Object 沒有 length()

        System.out.println("=== 2. 轉型(cast)與 ClassCastException ===");
        String s = (String) x;     // 向下轉型:告訴編譯器「相信我,它其實是 String」
        System.out.println("  s.length() = " + s.length());
        try {
            Integer bad = (Integer) x;   // 編譯器放行,執行期才發現不對
            System.out.println(bad);
        } catch (ClassCastException e) {
            System.out.println("  攔到 ClassCastException:" + e.getMessage());
        }
        // 安全的做法:先用 instanceof 檢查(Java 16+ 的「模式比對」還能順便宣告變數)
        if (y instanceof Integer n) {
            System.out.println("  y 是 Integer,加 1 = " + (n + 1));
        }

        System.out.println("=== 3. 泛型:型別安全,不必轉型 ===");
        GuestList<String> names = new ArrayGuestList<>();   // <> 讓編譯器推論型別參數
        names.add("Fabian");
        names.add("Nicholas");
        names.add("Fabian");                                  // 集合語意:不重複
        // names.add(42);          // 編譯錯誤!42 不是 String:錯誤在編譯期就被抓到
        assert names.count() == 2;
        System.out.println("  names.count() = " + names.count());

        GuestList<Integer> ids = new ArrayGuestList<>();
        ids.add(1001);
        ids.add(1002);
        assert ids.isComing(1001) && !ids.isComing(9999);
        System.out.println("  ids.isComing(1001) = " + ids.isComing(1001));

        System.out.println("=== 4. 型別擦除的陷阱 ===");
        Object[] ok = names.coming();          // OK:執行期的陣列真的是 Object[]
        System.out.println("  Object[] ok = " + Arrays.toString(ok));
        try {
            String[] boom = names.coming();    // 編譯通過,但執行期插入的 checkcast String[] 會失敗
            System.out.println(boom.length);
        } catch (ClassCastException e) {
            System.out.println("  String[] boom = names.coming() → ClassCastException!");
            System.out.println("  (T[] 在執行期被擦除成 Object[],這是 Java 泛型的真實限制)");
        }
        List<String> safe = ((ArrayGuestList<String>) names).comingList();
        System.out.println("  正解 comingList() = " + safe + "(用 List<T> 就沒有這個問題)");

        System.out.println("=== 5. Collections API:List ===");
        List<String> fruits = new ArrayList<>();               // 宣告用介面 List,實作用 ArrayList
        fruits.add("banana");
        fruits.add("apple");
        fruits.add("cherry");
        fruits.add("apple");                                   // List 允許重複、保持順序
        System.out.println("  fruits = " + fruits + ",size = " + fruits.size());
        System.out.println("  get(1) = " + fruits.get(1) + ",contains(cherry) = " + fruits.contains("cherry"));
        Collections.sort(fruits);
        System.out.println("  排序後 = " + fruits);
        fruits.remove("apple");                                // 只移除第一個
        System.out.println("  remove(apple) → " + fruits);
        for (String f : fruits) {                              // for-each:走訪任何 Collection
            System.out.println("    - " + f);
        }

        System.out.println("=== 6. Collections API:Set ===");
        Set<String> guests = new TreeSet<>();                  // TreeSet:自動排序、不重複
        guests.add("Liam");
        guests.add("Thomas");
        guests.add("Alice");
        guests.add("Liam");                                    // 重複:無效
        System.out.println("  TreeSet = " + guests + "(排序)");
        Set<String> hashGuests = new HashSet<>(guests);        // HashSet:更快,但順序不保證
        System.out.println("  HashSet contains(Alice) = " + hashGuests.contains("Alice"));
        assert guests.size() == 3 && hashGuests.size() == 3;

        System.out.println("=== 7. Collections API:Map ===");
        Map<String, Integer> ages = new HashMap<>();           // 鍵 → 值
        ages.put("Liam", 30);
        ages.put("Thomas", 25);
        ages.put("Liam", 31);                                  // 同一個鍵:覆蓋舊值
        System.out.println("  ages.get(Liam) = " + ages.get("Liam") + ",get(Nobody) = " + ages.get("Nobody"));
        System.out.println("  getOrDefault(Nobody, -1) = " + ages.getOrDefault("Nobody", -1));
        for (Map.Entry<String, Integer> e : new TreeMap<>(ages).entrySet()) {  // TreeMap:依鍵排序走訪
            System.out.println("    " + e.getKey() + " → " + e.getValue());
        }
        // 經典應用:計數
        Map<Character, Integer> freq = new TreeMap<>();
        for (char c : "mississippi".toCharArray()) {
            freq.put(c, freq.getOrDefault(c, 0) + 1);
        }
        System.out.println("  字元頻率 = " + freq);
        assert freq.get('s') == 4;

        System.out.println("=== 8. of() 工廠方法:不可變集合 ===");
        List<Integer> nums = List.of(3, 1, 4, 1, 5);
        try {
            nums.add(9);
        } catch (UnsupportedOperationException e) {
            System.out.println("  List.of(...) 是不可變的:add 會拋出 UnsupportedOperationException");
        }
        List<Integer> mutable = new ArrayList<>(nums);         // 要可變就複製一份
        mutable.add(9);
        System.out.println("  new ArrayList<>(nums) 之後 add(9) → " + mutable);

        System.out.println("=== 9. 泛型方法與 record ===");
        System.out.println("  firstOrDefault([], \"none\") = " + firstOrDefault(new ArrayList<String>(), "none"));
        System.out.println("  max([3,1,4,1,5]) = " + max(nums));
        System.out.println("  max([\"pear\",\"apple\"]) = " + max(List.of("pear", "apple")));
        Pair<String, Integer> p = Pair.of("Liam", 30);
        System.out.println("  p = " + p + ",p.swap() = " + p.swap());
        assert p.equals(new Pair<>("Liam", 30));               // record 自動有正確的 equals

        System.out.println("=== 10. 自動裝箱的陷阱:Integer 請用 equals 比較 ===");
        Integer a = 127, b = 127, c = 1000, d = 1000;
        System.out.println("  127 == 127 → " + (a == b) + "(小整數被快取,碰巧相同)");
        System.out.println("  1000 == 1000 → " + (c == d) + "(不同物件!)");
        System.out.println("  1000.equals(1000) → " + c.equals(d));
        System.out.println("全部完成。");
    }
}

Related articles