Warum sollte eine Enum eine Schnittstelle implementieren?

Antworten:

130

Aufzählungen müssen nicht nur passive Mengen (z. B. Farben) darstellen. Sie können komplexere Objekte mit Funktionalität darstellen und so sind Sie dann wahrscheinlich weitere Funktionalität zu diesem hinzufügen wollen - zB Sie Schnittstellen haben können wie Printable, Reportableusw. und Komponenten , die diese unterstützen.

Brian Agnew
quelle
259

Hier ist ein Beispiel (ein ähnliches / besseres findet sich in Effective Java 2nd Edition):

public interface Operator {
    int apply (int a, int b);
}

public enum SimpleOperators implements Operator {
    PLUS { 
        int apply(int a, int b) { return a + b; }
    },
    MINUS { 
        int apply(int a, int b) { return a - b; }
    };
}

public enum ComplexOperators implements Operator {
    // can't think of an example right now :-/
}

So erhalten Sie eine Liste der beiden einfachen + komplexen Operatoren:

List<Operator> operators = new ArrayList<Operator>();

operators.addAll(Arrays.asList(SimpleOperators.values()));
operators.addAll(Arrays.asList(ComplexOperators.values()));

Hier verwenden Sie also eine Schnittstelle, um erweiterbare Aufzählungen zu simulieren (was ohne die Verwendung einer Schnittstelle nicht möglich wäre).

Hilfsmethode
quelle
3
Komplexe Operatoren könnten "pow" und dergleichen sein.
Pimgd
2
Zum ersten Mal habe ich diese Enum-Syntax gesehen (mit den geschweiften Klammern nach den Mitgliedern), und ich programmiere seit 6 Jahren mit Java. Bis.
Jrahhali
23

Das ComparableBeispiel, das hier von mehreren Personen gegeben wurde, ist falsch, da dies Enumbereits umgesetzt wird. Sie können es nicht einmal überschreiben.

Ein besseres Beispiel ist eine Schnittstelle, die beispielsweise einen Datentyp definiert. Sie können eine Aufzählung zum Implementieren der einfachen Typen und normale Klassen zum Implementieren komplizierter Typen haben:

interface DataType {
  // methods here
}

enum SimpleDataType implements DataType {
  INTEGER, STRING;

  // implement methods
}

class IdentifierDataType implements DataType {
  // implement interface and maybe add more specific methods
}
Jorn
quelle
2
Ja. Seltsam !!. Enum implementiert bereits die Schnittstelle Comparable und erlaubt kein Überschreiben. Ich habe den Java-Quellcode für die Enum-Klasse nachgeschlagen und konnte die compareTo-Implementierung nicht finden. Kann mir jemand sagen, was in einer compareTo-Methode mit einer ENUM verglichen wird?
AKh
2
@AKh vergleicht die Ordnungszahlen, was bedeutet, dass die natürliche Reihenfolge die Reihenfolge ist, in der sich die Enum-Konstanten in der Quelldatei befinden.
Jorn
Enum vars sind endgültig und so erfolgt der Vergleich mit ==, richtig?
Djangofan
@ Djangofan ja.
Jorn
17

Es gibt einen Fall, den ich oft benutze. Ich habe eine IdUtilKlasse mit statischen Methoden, um mit Objekten zu arbeiten, die eine sehr einfache IdentifiableSchnittstelle implementieren:

public interface Identifiable<K> {
    K getId();
}


public abstract class IdUtil {

    public static <T extends Enum<T> & Identifiable<S>, S> T get(Class<T> type, S id) {
        for (T t : type.getEnumConstants()) {
            if (Util.equals(t.getId(), id)) {
                return t;
            }
        }
        return null;
    }

    public static <T extends Enum<T> & Identifiable<S>, S extends Comparable<? super S>> List<T> getLower(T en) {
        List<T> list = new ArrayList<>();
        for (T t : en.getDeclaringClass().getEnumConstants()) {
             if (t.getId().compareTo(en.getId()) < 0) {
                 list.add(t);
            }
        }
        return list;
    }
}

Wenn ich ein Identifiable enum:

    public enum MyEnum implements Identifiable<Integer> {

        FIRST(1), SECOND(2);

        private int id;

        private MyEnum(int id) {
            this.id = id;
        }

        public Integer getId() {
            return id;
        }

    }

Dann kann ich es idso bekommen:

MyEnum e = IdUtil.get(MyEnum.class, 1);
Sinuhepop
quelle
13

Da Enums Schnittstellen implementieren können, können sie zur strikten Durchsetzung des Singleton-Musters verwendet werden. Der Versuch, eine Standardklasse zu einem Singleton zu machen, ermöglicht ...

  • für die Möglichkeit, Reflexionstechniken zu verwenden, um private Methoden als öffentlich zu entlarven
  • für das Erben von Ihrem Singleton und das Überschreiben der Methoden Ihres Singletons mit etwas anderem

Aufzählungen als Singletons helfen, diese Sicherheitsprobleme zu vermeiden. Dies könnte einer der Gründe dafür gewesen sein, dass Enums als Klassen fungieren und Schnittstellen implementieren. Nur eine Vermutung.

Weitere Informationen finden Sie unter /programming/427902/java-enum-singleton und der Singleton-Klasse in Java .

Tansir1
quelle
1
for inheriting from your singleton and overriding your singleton's methods with something else. Sie können nur ein verwenden final class, um das zu verhindern
Dylanthepiguy
10

Dies ist für die Erweiterbarkeit erforderlich. Wenn jemand eine von Ihnen entwickelte API verwendet, sind die von Ihnen definierten Aufzählungen statisch. Sie können nicht hinzugefügt oder geändert werden. Wenn Sie jedoch eine Schnittstelle implementieren lassen, kann die Person, die die API verwendet, mithilfe derselben Schnittstelle eine eigene Aufzählung erstellen. Sie können diese Aufzählung dann bei einem Aufzählungsmanager registrieren, der die Aufzählungen zusammen mit der Standardschnittstelle zusammenfasst.

Edit: @Helper Method hat das perfekte Beispiel dafür. Stellen Sie sich vor, andere Bibliotheken definieren neue Operatoren und teilen dann einer Manager-Klasse mit, dass "Hey, diese Aufzählung existiert - registrieren Sie sie". Andernfalls können Sie Operatoren nur in Ihrem eigenen Code definieren - es gibt keine Erweiterbarkeit.

Chris Dennett
quelle
7

Aufzählungen sind nur verkleidete Klassen, also zum größten Teil alles, was Sie mit einer Klasse tun können, können Sie mit einer Aufzählung tun.

Ich kann mir keinen Grund vorstellen, warum eine Aufzählung keine Schnittstelle implementieren kann, und gleichzeitig kann ich mir auch keinen guten Grund dafür vorstellen.

Ich würde sagen, sobald Sie anfangen, Dinge wie Schnittstellen oder Methoden zu einer Aufzählung hinzuzufügen, sollten Sie wirklich darüber nachdenken, sie stattdessen zu einer Klasse zu machen. Natürlich bin ich mir sicher, dass es gültige Fälle für nicht-traditionelle Aufzählungssachen gibt, und da die Grenze künstlich wäre, bin ich dafür, die Leute dort tun zu lassen, was sie wollen.

TofuBeer
quelle
4
"... also zum größten Teil alles, was man mit einer Klasse machen kann, kann man mit einer Aufzählung machen", aber ich kann nicht sagen, dass meine Aufzählung von einer abstrakten Klasse geerbt wird. Also ja, Betonung auf "größtenteils".
Adam Parkin
5
Dies liegt daran, dass Enums alle java.lang.Enum erweitern und Java-Klassen nur von einer Klasse aus erweitert werden können.
TofuBeer
6

Zum Beispiel, wenn Sie eine Logger-Aufzählung haben. Dann sollten Sie die Logger-Methoden wie Debug, Info, Warnung und Fehler in der Schnittstelle haben. Dadurch wird Ihr Code lose gekoppelt.

Johan
quelle
6

Am häufigsten wird dies verwendet, um die Werte von zwei Aufzählungen zu einer Gruppe zusammenzuführen und sie ähnlich zu behandeln. Sehen Sie sich beispielsweise an, wie Sie Obst und Gemüse verbinden .

husayt
quelle
5

Einer der besten Anwendungsfälle für mich, Enums mit Schnittstelle zu verwenden, sind Prädikatfilter. Dies ist eine sehr elegante Methode, um die mangelnde Typizität von Apache-Sammlungen zu beheben (falls andere Bibliotheken möglicherweise nicht verwendet werden).

import java.util.ArrayList;
import java.util.Collection;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;


public class Test {
    public final static String DEFAULT_COMPONENT = "Default";

    enum FilterTest implements Predicate {
        Active(false) {
            @Override
            boolean eval(Test test) {
                return test.active;
            }
        },
        DefaultComponent(true) {
            @Override
            boolean eval(Test test) {
                return DEFAULT_COMPONENT.equals(test.component);
            }
        }

        ;

        private boolean defaultValue;

        private FilterTest(boolean defautValue) {
            this.defaultValue = defautValue;
        }

        abstract boolean eval(Test test);

        public boolean evaluate(Object o) {
            if (o instanceof Test) {
                return eval((Test)o);
            }
            return defaultValue;
        }

    }

    private boolean active = true;
    private String component = DEFAULT_COMPONENT;

    public static void main(String[] args) {
        Collection<Test> tests = new ArrayList<Test>();
        tests.add(new Test());

        CollectionUtils.filter(tests, FilterTest.Active);
    }
}
Anton Bessonov
quelle
5

Der obige Beitrag, in dem Strategien erwähnt wurden, hat nicht genug betont, was eine schöne, leichte Implementierung des Strategiemusters unter Verwendung von Aufzählungen bringt:

public enum Strategy {

  A {
    @Override
    void execute() {
      System.out.print("Executing strategy A");
    }
  },

  B {
    @Override
    void execute() {
      System.out.print("Executing strategy B");
    }
  };

  abstract void execute();
}

Sie können alle Ihre Strategien an einem Ort haben, ohne für jede eine separate Kompilierungseinheit zu benötigen. Sie erhalten einen schönen dynamischen Versand nur mit:

Strategy.valueOf("A").execute();

Lässt Java fast wie eine schöne, lose getippte Sprache lesen!

Robert Moskal
quelle
3

Aufzählungen sind wie Java-Klassen, sie können Konstruktoren, Methoden usw. haben. Das einzige, was Sie damit nicht tun können, ist new EnumName(). Die Instanzen sind in Ihrer Enum-Deklaration vordefiniert.

Cosmin Cosmin
quelle
Was ist mit enum Foo extends SomeOtherClass? Also nicht ganz das Gleiche wie eine reguläre Klasse, in der Tat ganz anders.
Adam Parkin
@Adam: Ich würde sagen, dass die Art und Weise, wie Enums Klassen ähneln, viel zahlreicher ist als die Art und Weise, wie sie sich von Klassen unterscheiden. Aber nein, sie sind nicht identisch.
Scottb
Wenn Sie eine Aufzählung dekompilieren, sehen Sie, dass es sich um eine Klasse handelt, die die Aufzählung erweitert und deren "Konstanten" bereits in Konstruktor- / Initialisierungsfeldern instanziiert sind.
Cosmin Cosmin
3

Eine andere Möglichkeit:

public enum ConditionsToBeSatisfied implements Predicate<Number> {
    IS_NOT_NULL(Objects::nonNull, "Item is null"),
    IS_NOT_AN_INTEGER(item -> item instanceof Integer, "Item is not an integer"),
    IS_POSITIVE(item -> item instanceof Integer && (Integer) item > 0, "Item is negative");

    private final Predicate<Number> predicate;
    private final String notSatisfiedLogMessage;

    ConditionsToBeSatisfied(final Predicate<Number> predicate, final String notSatisfiedLogMessage) {
        this.predicate = predicate;
        this.notSatisfiedLogMessage = notSatisfiedLogMessage;
    }

    @Override
    public boolean test(final Number item) {
        final boolean isNotValid = predicate.negate().test(item);

        if (isNotValid) {
            log.warn("Invalid {}. Cause: {}", item, notSatisfiedLogMessage);
        }

        return predicate.test(item);
    }
}

und mit:

Predicate<Number> p = IS_NOT_NULL.and(IS_NOT_AN_INTEGER).and(IS_POSITIVE);
Jorge Aranda
quelle
3

Beim Erstellen von Konstanten in einer JAR-Datei ist es häufig hilfreich, Benutzer Enum-Werte erweitern zu lassen. Wir haben Enums für PropertyFile-Schlüssel verwendet und sind hängen geblieben, weil niemand neue hinzufügen konnte! Unten hätte viel besser funktioniert.

Gegeben:

public interface Color {
  String fetchName();
}

und:

public class MarkTest {

  public static void main(String[] args) {
    MarkTest.showColor(Colors.BLUE);
    MarkTest.showColor(MyColors.BROWN);
  }

  private static void showColor(Color c) {
    System.out.println(c.fetchName());
  }
}

man könnte eine Aufzählung im Glas haben:

public enum Colors implements Color {
  BLUE, RED, GREEN;
  @Override
  public String fetchName() {
    return this.name();
  }
}

und ein Benutzer könnte es erweitern, um seine eigenen Farben hinzuzufügen:

public enum MyColors implements Color {
  BROWN, GREEN, YELLOW;
  @Override
  public String fetchName() {
    return this.name();
  }
}
markthegrea
quelle
2

Hier ist mein Grund warum ...

Ich habe eine JavaFX ComboBox mit den Werten einer Aufzählung gefüllt. Ich habe eine identifizierbare Schnittstelle (Angabe einer Methode: Identifizieren), mit der ich angeben kann, wie sich ein Objekt für Suchzwecke in meiner Anwendung identifiziert. Über diese Schnittstelle kann ich Listen aller Arten von Objekten (unabhängig davon, welches Feld das Objekt für die Identität verwendet) nach einer Identitätsübereinstimmung durchsuchen.

Ich möchte eine Übereinstimmung für einen Identitätswert in meiner ComboBox-Liste finden. Um diese Funktion in meiner ComboBox mit den Enum-Werten verwenden zu können, muss ich in der Lage sein, die identifizierbare Schnittstelle in meiner Enum zu implementieren (was im Fall einer Enum trivial zu implementieren ist).

Scottb
quelle
1

Ich habe eine innere Aufzählung in einer Schnittstelle verwendet, die eine Strategie beschreibt, um die Instanzkontrolle (jede Strategie ist ein Singleton) von dort aus zu behalten.

public interface VectorizeStrategy {

    /**
     * Keep instance control from here.
     * 
     * Concrete classes constructors should be package private.
     */
    enum ConcreteStrategy implements VectorizeStrategy {
        DEFAULT (new VectorizeImpl());

        private final VectorizeStrategy INSTANCE;

        ConcreteStrategy(VectorizeStrategy concreteStrategy) {
            INSTANCE = concreteStrategy;
        }

        @Override
        public VectorImageGridIntersections processImage(MarvinImage img) {
            return INSTANCE.processImage(img);
        }
    }

    /**
     * Should perform edge Detection in order to have lines, that can be vectorized.
     * 
     * @param img An Image suitable for edge detection.
     * 
     * @return the VectorImageGridIntersections representing img's vectors 
     * intersections with the grids.
     */
    VectorImageGridIntersections processImage(MarvinImage img);
}

Die Tatsache, dass die Aufzählung die Strategie implementiert, ist praktisch, damit die Aufzählungsklasse als Proxy für ihre eingeschlossene Instanz fungieren kann. das implementiert auch die Schnittstelle.

Es ist eine Art StrategieEnumProxy: P Der Clent-Code sieht folgendermaßen aus:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

Wenn es die Schnittstelle nicht implementiert hätte, wäre es gewesen:

VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);
juanmf
quelle