Java Language Se référant au type générique déclaré dans sa propre déclaration


Exemple

Comment allez-vous utiliser une instance d'un type générique (éventuellement supplémentaire) hérité dans une déclaration de méthode dans le type générique lui-même déclaré? C'est l'un des problèmes que vous rencontrerez lorsque vous approfondirez un peu les génériques, mais que vous rencontrerez un problème assez commun.

Supposons que nous ayons un type DataSeries<T> (interface ici), qui définit une série de données générique contenant des valeurs de type T Il est fastidieux de travailler directement avec ce type lorsque nous souhaitons effectuer de nombreuses opérations avec, par exemple, des valeurs doubles. Nous définissons donc DoubleSeries extends DataSeries<Double> . Supposons maintenant que le type DataSeries<T> ait une méthode add(values) qui ajoute une autre série de la même longueur et en renvoie une nouvelle. Comment pouvons-nous appliquer le type de values et le type de retour à DoubleSeries plutôt qu'à DataSeries<Double> dans notre classe dérivée?

Le problème peut être résolu en ajoutant un paramètre de type générique renvoyant à et étendant le type en cours de déclaration (appliqué à une interface ici, mais il en va de même pour les classes):

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

Ici, T représente le type de données que la série contient, par exemple Double et DS la série elle-même. Un type hérité (ou des types) peut maintenant être facilement implémenté en substituant le paramètre mentionné ci-dessus par un type dérivé correspondant, produisant ainsi une définition concrète à Double base du formulaire:

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

À ce moment, même un IDE implémentera l'interface ci-dessus avec les types corrects en place, ce qui, après un peu de remplissage de contenu, pourrait ressembler à ceci:

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);
    }
}

Comme vous pouvez le voir, la méthode add est déclarée en tant que DoubleSeries add(DoubleSeries values) et le compilateur est satisfait.

Le modèle peut être imbriqué si nécessaire.