在其自己的声明中引用声明的泛型类型

你如何在声明的泛型类型中的方法声明中使用(可能是进一步的)继承泛型类型的实例?这是你在深入研究泛型时会遇到的问题之一,但仍然是一个相当普遍的问题。

假设我们有一个 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) 并且编译器很高兴。

如果需要,可以进一步嵌套模式。