[JAVA] Punkt 38: Emulieren erweiterbarer Enums mit Schnittstellen

38. Die erweiterbare Aufzählung sollte in der Schnittstelle nachgeahmt werden

In den meisten Fällen ist die Idee, einen Aufzählungstyp zu erweitern, schlecht, wird jedoch nur beim Schreiben von "Operationscode" dringend benötigt. Es gibt einen guten Weg, dies mit enum zu tun. Da enum eine Schnittstelle implementieren kann, kann dies beispielsweise wie folgt erfolgen.

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

Der Aufzählungstyp ist nicht erweiterbar, aber die Schnittstelle ist erweiterbar und es ist die Schnittstelle, die als API für Operationen verwendet wird. Sie können diese Schnittstelle auch verwenden, um einen neuen Operationscode wie folgt zu definieren:

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

Eine erweiterte Enum-Instanz kann überall dort verwendet werden, wo die zugrunde liegende Enum-Instanz erwartet wird. Darüber hinaus können Sie in Situationen, in denen Sie alle Elemente der Basisaufzählung übergeben, stattdessen alle Elemente der erweiterten Aufzählung übergeben. Der folgende Quellcode zeigt das Ergebnis aller Operationen durch "ExtendedOperation" der beiden als Argumente verwendeten Werte an.

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

}

In diesem Code wird "ExtendedOperation.class" als erstes Argument der Testmethode verwendet. Dies ist ein Token vom begrenzten Typ (Item33). <t extends enum<t> & operation>Die Klasse, die dem ersten Argument des Tests dadurch gegeben wird, ist jedoch enum undoperationIst garantiert eine Unterklasse von. Sie können auch den begrenzten Platzhaltertyp (Item31) verwenden, um Folgendes zu schreiben:

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

Dieser Code ist etwas prägnanter und flexibler, aber Sie sollten die Verwendung von "EnumSet" (Item36) und "EnumMap" (Item37) aufgeben. Die obigen zwei Codes werden wie folgt ausgegeben, wenn "4 2" als Argument angegeben wird.

4.000000 ^ 2.000000 = 16.000000
4.000000 % 2.000000 = 0.000000

Der kleine Nachteil dieser erweiterten Aufzählung besteht darin, dass der Code, in dem die mit der Operation verknüpften Symbole und der zu suchende Code gespeichert sind, sowohl in "BasicOperation" als auch in "ExtendedOperation" beibehalten werden muss. .. Wenn zu viele allgemeine Funktionen angezeigt werden, müssen Hilfsklassen usw. vorbereitet werden, um Codeduplikationen zu vermeiden. Die in diesem Kapitel erwähnten Muster werden auch in `` `java.nio.file.LinkOption``` verwendet.

Recommended Posts

Punkt 38: Emulieren erweiterbarer Enums mit Schnittstellen
Mock Enum mit PowerMock
Punkt 65: Schnittstellen der Reflexion vorziehen