[JAVA] Point 38: émuler des énumérations extensibles avec des interfaces

38. L'énumération extensible doit être imitée dans l'interface

Dans la plupart des cas, l'idée d'étendre un type d'énumération est mauvaise, mais elle n'est nécessaire de toute urgence que lors de l'écriture de operation code. Il existe un bon moyen de faire cela avec enum. Puisque enum peut implémenter une interface, cela peut être fait comme suit, par exemple.

// Emulated extensible enum using an interface
public interface Operation {
    double apply(double x, double y);
}

public enum BasicOperation implements Operation {
    PLUS("+") {
        public double apply(double x, double y) {
            return x + y;
        }
    },
    MINUS("-") {
        public double apply(double x, double y) {
            return x - y;
        }
    },
    TIMES("*") {
        public double apply(double x, double y) {
            return x * y;
        }
    },
    DIVIDE("/") {
        public double apply(double x, double y) {
            return x / y;
        }
    };
    private final String symbol;

    BasicOperation(String symbol) {
        this.symbol = symbol;
    }

    @Override
    public String toString() {
        return symbol;
    }
}

Le type enum n'est pas extensible, mais l'interface est extensible et c'est l'interface qui est utilisée comme API pour les opérations. Vous pouvez également utiliser cette interface pour définir un nouveau code opération comme suit:

// Emulated extension enum
public enum ExtendedOperation implements Operation {
    EXP("^") {
        public double apply(double x, double y) {
            return Math.pow(x, y);
        }
    },
    REMAINDER("%") {
        public double apply(double x, double y) {
            return x % y;
        }
    };
    private final String symbol;

    ExtendedOperation(String symbol) {

        this.symbol = symbol;
    }

    @Override
    public String toString() {
        return symbol;
    }
}

Une instance enum étendue peut être utilisée partout où l'instance enum sous-jacente est attendue. Non seulement cela, mais dans les situations où vous souhaitez passer tous les éléments de l'énumération de base, vous pouvez passer tous les éléments de l'énumération étendue à la place. Le code source suivant affiche le résultat de toutes les opérations par ExtendedOperation des deux valeurs prises comme arguments.

public static void main(String[] args) {
    double x = Double.parseDouble(args[0]);
    double y = Double.parseDouble(args[1]);
    test(ExtendedOperation.class, x, y);
}

private static <T extends Enum<T> & Operation> void test(Class<T> opEnumType, double x, double y) {
    for (Operation op : opEnumType.getEnumConstants())
        System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));

}

Dans ce code, ExtendedOperation.class est considéré comme le premier argument de la méthode de test. Il s'agit d'un jeton de type borné (Item33). <t extends enum<t> & operation>Cependant, la classe donnée au premier argument de test par this est enum etoperationEst garanti être une sous-classe de. Vous pouvez également utiliser le type générique borné (Item31) pour écrire:

public static void main(String[] args) {
    double x = Double.parseDouble(args[0]);
    double y = Double.parseDouble(args[1]);
    test(Arrays.asList(ExtendedOperation.values()), x, y);
}
private static void test(Collection<? extends Operation> opSet, double x, double y) {
    for (Operation op : opSet) {
        System.out.printf("%f%s%f=%f%s", x, op, y, op.apply(x, y));
    }
}

Ce code est un peu plus concis et flexible, mais vous devriez renoncer à utiliser EnumSet (Item36) et EnumMap (Item37). Les deux codes ci-dessus sont affichés comme suit lorsque `` 4 2 '' est donné comme argument.

4.000000 ^ 2.000000 = 16.000000
4.000000 % 2.000000 = 0.000000

L'inconvénient mineur de cette énumération étendue est que le code qui stocke le symbole associé à l'opération et le code qui recherche doit être conservé à la fois dans BasicOperation``` etExtendedOperation. .. Si trop de fonctions communes apparaissent, il est nécessaire de préparer des classes d'assistance, etc. pour éliminer la duplication de code. Les modèles mentionnés dans ce chapitre sont également utilisés dans `` `` java.nio.file.LinkOption.

Recommended Posts

Point 38: émuler des énumérations extensibles avec des interfaces
Mock Enum avec PowerMock
Rubrique 65: Préférez les interfaces à la réflexion