S SmartDocs
系列: Angelia java 87 行 · 更新于 2026-09-27

ArrayGuestList.java

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

import java.util.Arrays;

/**
 * 泛型版的「嚴格」陣列實作。
 *
 * 型別擦除(type erasure)的限制:執行期不知道 T 是什麼,所以「不能」寫 new T[n]。
 * 標準做法:內部用 Object[] 存,取出時再轉型成 T。
 * 只要我們靠封裝保證「陣列裡只放過 T」,這個「未受檢查的轉型」在實務上就是安全的。
 *
 * 資料不變量:data[0..used-1] 非 null 且互不相同(用 equals 比較)。
 */
public class ArrayGuestList<T> implements GuestList<T> {

    private Object[] data = new Object[2];   // 不能是 T[]!
    private int used = 0;

    @Override
    public void add(T guest) {
        if (guest == null) {
            throw new IllegalArgumentException("賓客不可為 null");
        }
        if (isComing(guest)) {
            return;
        }
        if (this.used == this.data.length) {
            this.data = Arrays.copyOf(this.data, this.used * 2);   // 標準函式庫幫我們倍增+複製
        }
        this.data[this.used] = guest;
        this.used = this.used + 1;
    }

    @Override
    public void remove(T guest) {
        int i = 0;
        while (i < this.used) {
            if (this.data[i].equals(guest)) {
                // 用最後一個元素填補洞,保持 data[0..used-1] 沒有 null
                this.data[i] = this.data[this.used - 1];
                this.data[this.used - 1] = null;
                this.used = this.used - 1;
                return;
            }
            i = i + 1;
        }
    }

    @Override
    public boolean isComing(T guest) {
        int i = 0;
        while (i < this.used) {
            if (this.data[i].equals(guest)) {
                return true;
            }
            i = i + 1;
        }
        return false;
    }

    @Override
    public int count() {
        return this.used;
    }

    /**
     * ★ 型別擦除的陷阱:這個陣列在執行期「真的」是 Object[]。
     * 呼叫端若寫 String[] s = list.coming(); 會在執行期拋出 ClassCastException!
     * (寫 Object[] s = list.coming(); 則沒問題。)見 Demo.java 的示範與 comingList() 的正解。
     */
    @Override
    @SuppressWarnings("unchecked")   // 告訴編譯器:我知道這個轉型沒被檢查,我負責它的安全
    public T[] coming() {
        // Arrays.copyOf 回傳的是新陣列(防禦性複製);(T[]) 是「未受檢查的轉型」
        return (T[]) Arrays.copyOf(this.data, this.used);
    }

    /** 正解:回傳 List<T>。泛型集合沒有陣列的執行期型別問題,這也是標準函式庫的做法。 */
    @SuppressWarnings("unchecked")
    public java.util.List<T> comingList() {
        java.util.List<T> result = new java.util.ArrayList<>();
        int i = 0;
        while (i < this.used) {
            result.add((T) this.data[i]);   // 單一元素的轉型:執行期什麼都不檢查(T 已被擦除)
            i = i + 1;
        }
        return result;
    }
}

相关文章