Dans la classe générique Java, ajoutez des contraintes génériques supplémentaires au niveau du constructeur?


, j'ai une interface appelée Bar et une classe générique Foo paramétrée sur un type est un Bar:

class Foo<B extends Bar> { }

Ma classe a un constructeur à usage général qui prend un Class et un Stream:

class Foo<B extends Bar> {
    B[] bs;
    Foo(Class<B> clazz, Stream<B> stream) { // General ctor
        bs = someFunctionOf(clazz, stream);
    }
}

J'essaie d'ajouter un constructeur spécialisé qui nécessite que son paramètre de méthode réel both soit un Bar et une classe enum telle que je puisse appeler mon constructeur à usage général à partir du constructeur spécial:

class Foo<B extends Bar> {
    B[] bs;
    Foo(Class<B> clazz, Stream<B> stream) { // General ctor
        bs = someFunctionOf(clazz, stream);
    }
    // FIX THIS ----+
    //              |
    //              ˅
    Foo(Class<Something> clazz) { // Special ctor
        // Can we make this work so Something is a Bar and an enum
        // and we can call the other constructor like this?
        this(clazz, Arrays.stream(clazz.getEnumConstants());
    }
}
Author: 0xbe5077ed, 2017-12-19

3 answers

De manière générale, vous pouvez écrire des constructeurs génériques. Nous avons récemment eu une question à leur sujet, et comment ils pourraient être utiles. De cette façon, vous pouvez fournir un constructeur qui prend comme argument une classe représentant une classe qui étend à la fois une autre classe spécifique et implémente l'interface Bar:

class Foo<B extends Bar> {
    B[] bs;
    Foo(Class<B> clazz, Stream<B> stream) { // General ctor
        bs = someFunctionOf(clazz, stream);
    }

    private B[] someFunctionOf(Class<B> clazz, Stream<B> stream) {
        return null;
    }

    <T extends SomeClass & Bar> Foo(Class<T> clazz) {
        // ...
    }
}

Mais cela ne vous amène pas tout à fait où vous voulez être, car les limites de l'argument de type du constructeur T doivent être des types explicites. Les variables de Type tel que le paramètre de type de la classe B ne sert pas, et sans moyen de connecter T à B, le constructeur générique spécial ne peut pas invoquer le constructeur général.

Mais vous pouvez le faire avec une méthode factory au lieu d'un constructeur spécial:

class Foo<B extends Bar> {
    B[] bs;
    Foo(Class<B> clazz, Stream<B> stream) { // General ctor
        bs = someFunctionOf(clazz, stream);
    }

    private B[] someFunctionOf(Class<B> clazz, Stream<B> stream) {
        return null;
    }

    static <T extends Enum<T> & Bar> Foo<T> createEnumFoo(Class<T> clazz) {
        return new Foo<>(clazz, Arrays.stream(clazz.getEnumConstants()));
    }
}
 6
Author: John Bollinger, 2017-12-19 15:36:53

Je ne pense pas que vous puissiez le faire avec le constructeur. Soit créer une sous-classe:

class EnumFoo<B extends Enum<B>&Bar> extends Foo<B> {
    public EnumFoo(Class<B> clazz) {
        super(clazz, Arrays.stream(clazz.getEnumConstants()));
    }
}

Ou méthode d'usine:

public static <T extends Enum<T>&Bar> Foo<T> of(Class<T> clazz) { 
    return new Foo<>(clazz, Arrays.stream(clazz.getEnumConstants()));
}
 5
Author: user158037, 2017-12-19 15:31:15

Ceci est possible en utilisant l'opérateur&: <Something extends Bar & Enum<?>>

 -4
Author: user1383093, 2017-12-19 17:46:31