Perché un Enum dovrebbe implementare un'interfaccia?


Risposte:


130

Gli enum non devono solo rappresentare set passivi (ad es. Colori). Possono rappresentare oggetti più complessi con funzionalità, quindi è probabile che tu voglia aggiungere ulteriori funzionalità a questi, ad esempio potresti avere interfacce come Printable, Reportableecc. E componenti che li supportano.


259

Ecco un esempio (uno simile / migliore si trova 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 :-/
}

Ora per ottenere un elenco di entrambi gli operatori Simple + Complex:

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

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

Quindi qui usi un'interfaccia per simulare enum estensibili (che non sarebbe possibile senza usare un'interfaccia).


3
Gli operatori complessi potrebbero essere "pow" e simili.
Pimgd,

2
La prima volta che ho visto quella sintassi di Enum (con le parentesi graffe dopo i membri), e ho programmato con Java per 6 anni. TIL.
jrahhali,

23

L' Comparableesempio fornito da diverse persone qui è sbagliato, poiché lo Enumimplementa già. Non puoi nemmeno ignorarlo.

Un esempio migliore è avere un'interfaccia che definisce, diciamo, un tipo di dati. Puoi avere un enum per implementare i tipi semplici e avere normali classi per implementare tipi complicati:

interface DataType {
  // methods here
}

enum SimpleDataType implements DataType {
  INTEGER, STRING;

  // implement methods
}

class IdentifierDataType implements DataType {
  // implement interface and maybe add more specific methods
}

2
Sì, strano !! Enum implementa già l'interfaccia Comparable e non consente di eseguire l'override. Ho cercato il codice sorgente Java per la classe enum e non sono riuscito a trovare l'implementazione di compareTo. Qualcuno può dirmi cosa viene confrontato all'interno di un metodo compareTo per un ENUM?
Akh

2
@AKh confronta gli ordinali, il che significa che l'ordine naturale è l'ordine in cui le costanti enum sono nel file sorgente.
Jorn,

Le variazioni Enum sono definitive e quindi il confronto avviene con ==, giusto?
Djangofan,

@djangofan si.
Jorn,

18

C'è un caso che uso spesso. Ho una IdUtilclasse con metodi statici per lavorare con oggetti che implementano un'interfaccia molto semplice Identifiable:

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

Se creo un 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;
        }

    }

Quindi posso ottenerlo in idquesto modo:

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

Esattamente quello di cui avevo bisogno!
Petronella,

13

Poiché Enums è in grado di implementare interfacce, possono essere utilizzate per applicare rigorosamente il modello singleton. Cercare di rendere una classe standard un singleton permette ...

  • per la possibilità di utilizzare tecniche di riflessione per esporre metodi privati ​​come pubblici
  • per ereditare dal tuo singleton e sovrascrivere i metodi del tuo singleton con qualcos'altro

Enum come singoli aiutano a prevenire questi problemi di sicurezza. Questo potrebbe essere stato uno dei motivi che hanno contribuito a far funzionare Enums come classi e implementare interfacce. Solo una supposizione.

Vedi /programming/427902/java-enum-singleton e le lezioni di Singleton in Java per ulteriori discussioni.


1
for inheriting from your singleton and overriding your singleton's methods with something else. puoi semplicemente usare a final classper impedirlo
Dylanthepiguy,

10

È richiesto per l'estensibilità: se qualcuno utilizza un'API che hai sviluppato, gli enumerati che definisci sono statici; non possono essere aggiunti o modificati. Tuttavia, se si consente di implementare un'interfaccia, la persona che utilizza l'API può sviluppare il proprio enum utilizzando la stessa interfaccia. È quindi possibile registrare questo enum con un gestore enum che aggrega gli enum insieme all'interfaccia standard.

Modifica: il metodo @Helper ha l'esempio perfetto di questo. Pensa di avere altre librerie che definiscono nuovi operatori e poi dicono a una classe manager che "ehi, questo enum esiste - registralo". Altrimenti, saresti in grado di definire gli Operatori solo nel tuo codice, senza estensibilità.


7

Gli enum sono solo classi mascherate, quindi per la maggior parte, tutto ciò che puoi fare con una classe che puoi fare con un enum.

Non riesco a pensare a una ragione per cui un enum non dovrebbe essere in grado di implementare un'interfaccia, allo stesso tempo non riesco a pensare a una buona ragione per loro.

Direi che una volta che inizi ad aggiungere cose come interfacce o metodi a un enum, dovresti davvero prendere in considerazione l'idea di renderlo una classe. Ovviamente sono sicuro che ci sono casi validi per fare cose enum non tradizionali e, dato che il limite sarebbe artificiale, sono favorevole a lasciare che le persone facciano ciò che vogliono lì.


4
"... quindi per la maggior parte tutto ciò che puoi fare con una classe che puoi fare con un enum", ma non posso dire che il mio enum erediti da una classe astratta. Quindi sì, l'enfasi su "per la maggior parte".
Adam Parkin,

5
Questo perché tutti gli enum estendono java.lang.Enum e le classi Java possono estendersi solo da una classe.
TofuBeer,

6

Ad esempio se si dispone di un enumerazione logger. Quindi dovresti avere i metodi del logger come debug, informazioni, avvertenze ed errori nell'interfaccia. Rende il tuo codice liberamente accoppiato.


6

L'uso più comune per questo sarebbe quello di unire i valori di due enumerazioni in un gruppo e trattarli in modo simile. Ad esempio, scopri come unirti a frutta e verdura .


5

Uno dei migliori casi di utilizzo di enum con l'interfaccia per me è rappresentato dai filtri Predicate. È un modo molto elegante per rimediare alla mancanza di tipicità delle raccolte di apache (se non è possibile utilizzare altre librerie).

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

5

Il post sopra che menziona le strategie non ha sottolineato abbastanza ciò che ti dà una bella implementazione leggera del modello di strategia che usa enums:

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

Puoi avere tutte le tue strategie in un unico posto senza bisogno di un'unità di compilazione separata per ognuna. Ricevi un bel invio dinamico solo con:

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

Fa leggere java quasi come un bel linguaggio vagamente digitato!


3

Gli Enum sono come le Classi Java, possono avere Costruttori, Metodi, ecc. L'unica cosa che non puoi fare con loro è new EnumName(). Le istanze sono predefinite nella dichiarazione enum.


Che dire enum Foo extends SomeOtherClass? Quindi non è esattamente la stessa cosa di una classe normale, in effetti, abbastanza diversa.
Adam Parkin,

@Adam: Direi che i modi in cui gli Enum assomigliano alle classi sono molto più numerosi dei modi in cui differiscono dalle classi. Ma no, non sono identici.
Scott

se decompili un enum vedrai che è una classe che estende l'Enumerazione e ha le "costanti" già istanziate nei campi costruttore / inizializzazione.
Cosmin Cosmin

3

Un'altra possibilità:

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

e usando:

Predicate<Number> p = IS_NOT_NULL.and(IS_NOT_AN_INTEGER).and(IS_POSITIVE);

3

Quando si creano costanti in un file jar, è spesso utile consentire agli utenti di estendere i valori enum. Abbiamo usato enum per le chiavi PropertyFile e siamo rimasti bloccati perché nessuno poteva aggiungerne di nuovi! Di seguito avrebbe funzionato molto meglio.

Dato:

public interface Color {
  String fetchName();
}

e:

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

si potrebbe avere un enum nel barattolo:

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

e un utente potrebbe estenderlo per aggiungere i suoi colori:

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

2

Ecco il mio motivo per cui ...

Ho popolato un ComboBox JavaFX con i valori di un Enum. Ho un'interfaccia, identificabile (specificando un metodo: identifica), che mi consente di specificare come ogni oggetto si identifica nella mia applicazione per scopi di ricerca. Questa interfaccia mi consente di scansionare elenchi di qualsiasi tipo di oggetti (qualunque campo l'oggetto possa usare per l'identità) per una corrispondenza di identità.

Vorrei trovare una corrispondenza per un valore di identità nell'elenco ComboBox. Per utilizzare questa funzionalità sul mio ComboBox contenente i valori Enum, devo essere in grado di implementare l'interfaccia identificabile nel mio Enum (che, come accade, è banale da implementare nel caso di un Enum).


1

Ho usato un enum interno in un'interfaccia che descrive una strategia per mantenere il controllo dell'istanza (ogni strategia è un Singleton) da lì.

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

Il fatto che l'enum attui la strategia è conveniente per consentire alla classe enum di fungere da proxy per la sua istanza inclusa. che implementa anche l'interfaccia.

è una sorta di strategiaEnumProxy: P il codice clent è simile al seguente:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

Se non avesse implementato l'interfaccia sarebbe stato:

VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.