web-dev-qa-db-fra.com

Puis-je faire une énumération abstraite en Java?

Vous trouverez ci-dessous une déclaration d'énumération valide.

public enum SomeEnumClass {

    ONE(1), TWO(2), THREE(3);

    private int someInt;

    public SomeEnumClass(int someInt) {
        this.someInt = someInt;
    }
}

Mais puis-je remplacer une classe abstraite par un type enum?

SomeEnumClass.Java

public abstract enum SomeEnumClass {

    private int someInt;

    public SomeEnumClass(int someInt) {
        this.someInt = someInt;
    }
}

OverridingEnumClass.Java

public enum OverridingEnumClass extends SomeEnumClass {

    ONE(1), TWO(2), THREE(3);

}

Sinon, pourquoi pas? Et quelle est une bonne alternative?

34
MC Emperor

Non, tu ne peux pas; les types enum étendent tous Enum, et ils sont implicitement final. Les énumérations peuvent implémenter des interfaces, ou vous pouvez déclarer les méthodes pertinentes directement sur la classe d'énumération en question.

(Je vois l'idée de base de ce que vous voulez, qui est un mixin; peut-être que les interfaces Java 8 seront un peu plus utiles à cet égard.)

35
chrylis

Dans Java vous ne pouvez pas étendre l'énumération ou créer une énumération abstraite ou même la générer. Si vous voulez avoir un point d'extension polymorphe sur votre énumération, vous pouvez appliquer un tel modèle.

Disons que votre énumération

public enum SomeEnumClass {
    ONE, TWO, THREE;
}

Et vous voulez avoir un comportement associé à chaque valeur. Mais vous ne voulez pas coder en dur chacun, mais plutôt avoir la possibilité d'en fournir un autre. Vous devez donc déclarer l'interface

public interface SomeEnumVisitor<P, R> {
     R one(P param);
     R two(P param);
     R three(P param);
}

Ajoutez ensuite une méthode abstraite à la déclaration d'énumération et à l'implémentation de cette méthode pour chaque valeur

public enum SomeEnumClass {
    ONE {
        @Override
        public <R, P> R accept(SomeEnumVisitor<R, P> visitor, P param) {
            return visitor.one(param);
        }
    }, TWO {
        @Override
        public <R, P> R accept(SomeEnumVisitor<R, P> visitor, P param) {
            return visitor.two(param);
        }
    }, THREE {
        @Override
        public <R, P> R accept(SomeEnumVisitor<R, P> visitor, P param) {
            return visitor.three(param);
        }
    };
    public abstract <R, P> R accept(SomeEnumVisitor<R, P> visitor, P param);
}

Afin que vous puissiez créer une implémentation de visiteur pour étendre votre comportement d'énumération. Par exemple, dans votre cas, vous vouliez associer une valeur entière à chaque valeur énumérée.

public class IntValueVisitor implements SomeClassVisitor<Integer, Void> {
     @Override
     public Integer one(Void param){
         return 1;
     }

     @Override
     public Integer two(Void param){
         return 2;
     }

     @Override
     public Integer three(Void param){
         return 3;
     }    
}

Et enfin, utilisez ce visiteur là où vous en avez besoin

SomeClassEnum something = getAnyValue();
// this expression will return your particular int value associated with particular enum.
int intValue = something.accept(new IntValueVisitor(), null);

Bien sûr, ce modèle s'applique s'il n'est pas approprié de tout déclarer à l'intérieur d'énum, ​​par exemple si vous avez une déclaration d'énumération dans la bibliothèque et que vous souhaitez étendre le comportement d'énumération dans l'application principale. Ou vous ne voulez simplement pas coupler la définition d'énumération et les détails d'implémentation.

Afin de simplifier cette implémentation de modèle il existe une bibliothèque qui peut générer une énumération et un visiteur basés sur des annotations afin que tout ce que vous devez déclarer dans votre code soit

@AutoEnum(value = {"one", "two", "three"}, name = "SomeEnumClass")
public interface SomeEnumMarker {}

L'outil se reposera pour vous.

9
rus1f1kat0R

Si vous devez vraiment "étendre une énumération", vous pouvez utiliser le modèle d'énumération Typesafe pré-Java 1.5 (voir le bas de http://www.javacamp.org/designPattern/enum.html ) qui utilise en fait une classe, pas une énumération. Vous perdez la possibilité d'utiliser l'EnumSet avec vos "enum" et vous perdez certaines méthodes générées automatiquement telles que items (), mais vous avez la possibilité de remplacer les méthodes.

Un exemple:

// Typesafe enum pattern
public static abstract class Operator {
    public static final Operator ADD = new Operator("+") {
        @Override
        public Double apply(Double firstParam, Double secondParam) {
            return firstParam + secondParam;
        }
    };
    public static final Operator SUB = new Operator("-") {
        @Override
        public Double apply(Double firstParam, Double secondParam) {
            return firstParam - secondParam;
        }
    };
    public static final Operator MUL = new Operator("*") {
        @Override
        public Double apply(Double firstParam, Double secondParam) {
            return firstParam * secondParam;
        }
    };
    public static final Operator DIV = new Operator("/") {
        @Override
        public Double apply(Double firstParam, Double secondParam) {
            return firstParam / secondParam;
        }
    };

    private static final Operator[] _ALL_VALUES = {ADD, SUB, MUL, DIV};
    private static final List<Operator> ALL_VALUES = Collections.unmodifiableList(Arrays.asList(_ALL_VALUES));

    private final String operation;

    private Operator(String c) {
        operation = c;
    }

    // Factory method pattern
    public static Operator fromToken(String operation) {
        for (Operator o : Operator.items())
            if (o.operation.equals(operation))
                return o;
        return null;
    }

    public Iterable<Operator> items() {
        return ALL_VALUES;
    }

    public abstract Double apply(Double firstParam, Double secondParam); 

}
4
Marcin