在其自己的宣告中引用宣告的泛型型別

你如何在宣告的泛型型別中的方法宣告中使用(可能是進一步的)繼承泛型型別的例項?這是你在深入研究泛型時會遇到的問題之一,但仍然是一個相當普遍的問題。

假設我們有一個 DataSeries<T> 型別(這裡是介面),它定義了一個包含 T 型別值的通用資料系列。當我們想要用例如雙值執行大量操作時直接使用這種型別很麻煩,所以我們定義 DoubleSeries extends DataSeries<Double>。現在假設,原始 DataSeries<T> 型別有一個方法 add(values),它新增另一個相同長度的系列並返回一個新的系列。我們如何在派生類中強制執行 values 的型別和返回的型別為 DoubleSeries 而不是 DataSeries<Double>

這個問題可以通過新增一個泛型型別引數來解決,該引數返回並擴充套件宣告的型別(在這裡應用於一個介面,但同樣代表類):

public interface DataSeries<T, DS extends DataSeries<T, DS>> {
    DS add(DS values);
    List<T> data();
}

這裡 T 代表系列所擁有的資料型別,例如 DoubleDS 系列本身。現在可以通過用相應的派生型別替換上述引數來輕鬆實現繼承型別(或多個型別),從而產生基於 Double 的具體形式定義:

public interface DoubleSeries extends DataSeries<Double, DoubleSeries> {
    static DoubleSeries instance(Collection<Double> data) {
        return new DoubleSeriesImpl(data);
    }
}

此時甚至 IDE 都會使用正確的型別來實現上面的介面,在一些內容填充後可能如下所示:

class DoubleSeriesImpl implements DoubleSeries {
    private final List<Double> data;

    DoubleSeriesImpl(Collection<Double> data) {
        this.data = new ArrayList<>(data);
    }

    @Override
    public DoubleSeries add(DoubleSeries values) {
        List<Double> incoming = values != null ? values.data() : null;
        if (incoming == null || incoming.size() != data.size()) {
            throw new IllegalArgumentException("bad series");
        }
        List<Double> newdata = new ArrayList<>(data.size());
        for (int i = 0; i < data.size(); i++) {
            newdata.add(this.data.get(i) + incoming.get(i)); // beware autoboxing
        }
        return DoubleSeries.instance(newdata);
    }

    @Override
    public List<Double> data() {
        return Collections.unmodifiableList(data);
    }
}

正如你所看到的,add 方法被宣告為 DoubleSeries add(DoubleSeries values) 並且編譯器很高興。

如果需要,可以進一步巢狀模式。