Por que um Enum implementaria uma interface?

193

Acabei de descobrir que Java permite que enums implementem uma interface. Qual seria um bom caso de uso para isso?

unj2
fonte
1
O que você precisa
saber é que você pode

Respostas:

130

As enums não precisam apenas representar conjuntos passivos (por exemplo, cores). Eles podem representar objetos mais complexos com funcionalidade e, portanto, é provável que você queira adicionar mais funcionalidades a eles - por exemplo, você pode ter interfaces como Printable, Reportableetc. , e componentes que os suportem.

Brian Agnew
fonte
259

Aqui está um exemplo (um similar / melhor é encontrado no 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 :-/
}

Agora, para obter uma lista dos dois operadores simples e complexos:

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

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

Então, aqui você usa uma interface para simular enums extensíveis (o que não seria possível sem o uso de uma interface).

helpermethod
fonte
3
Operadores complexos podem ser "preguiçosos" e similares.
Pimgd
2
É a primeira vez que vejo a sintaxe do Enum (com os chavetas depois dos membros), e estou programando com Java há 6 anos. TIL.
precisa saber é o seguinte
23

O Comparableexemplo dado por várias pessoas aqui está errado, pois Enumjá o implementa. Você nem pode substituí-lo.

Um exemplo melhor é ter uma interface que define, digamos, um tipo de dados. Você pode ter uma enumeração para implementar os tipos simples e ter classes normais para implementar tipos complicados:

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
fonte
2
Sim. Estranho !!. O Enum já implementa a interface Comparable e não permite substituir. Procurei o código-fonte java para a classe enum e não consegui encontrar a implementação compareTo. Alguém pode me dizer o que é comparado dentro de um método compareTo um ENUM?
AKh
2
@AKh compara os ordinais, o que significa que a ordem natural é a ordem em que as constantes enum estão no arquivo de origem.
Jorn
Vars enum são finais e, portanto, a comparação ocorre com ==, certo?
djangofan
@djangofan yes.
Jorn
17

Há um caso que eu frequentemente uso. Eu tenho uma IdUtilclasse com métodos estáticos para trabalhar com objetos implementando uma Identifiableinterface muito simples :

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 eu criar um 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;
        }

    }

Então eu posso obtê-lo iddesta forma:

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

Como os Enums podem implementar interfaces, eles podem ser usados ​​para impor rigorosamente o padrão singleton. Tentando fazer uma classe padrão um singleton permite ...

  • pela possibilidade de usar técnicas de reflexão para expor métodos privados como
  • por herdar do seu singleton e substituir os métodos do seu singleton por outra coisa

Enums como singletons ajudam a evitar esses problemas de segurança. Esse pode ter sido um dos motivos que contribuíram para permitir que o Enums atue como classes e implemente interfaces. Apenas um palpite.

Consulte /programming/427902/java-enum-singleton e a classe Singleton em java para obter mais discussão.

Tansir1
fonte
1
for inheriting from your singleton and overriding your singleton's methods with something else. você pode simplesmente usar um final classpara evitar que
Dylanthepiguy
10

É necessário extensibilidade - se alguém usa uma API que você desenvolveu, as enumerações que você define são estáticas; eles não podem ser adicionados ou modificados. No entanto, se você permitir implementar uma interface, a pessoa que usa a API pode desenvolver sua própria enumeração usando a mesma interface. Em seguida, você pode registrar essa enumeração com um gerenciador de enumerações que conglomera as enumerações junto com a interface padrão.

Edit: @Helper Method tem o exemplo perfeito disso. Pense em ter outras bibliotecas definindo novos operadores e depois dizendo a uma classe gerenciadora que 'ei, essa enum existe - registre-a'. Caso contrário, você só poderá definir Operadores em seu próprio código - não haverá extensibilidade.

Chris Dennett
fonte
7

Enums são apenas classes disfarçadas; portanto, na maior parte do tempo, qualquer coisa que você possa fazer com uma classe pode fazer com uma enumeração.

Não consigo pensar em uma razão para que um enum não consiga implementar uma interface, ao mesmo tempo não consigo pensar em uma boa razão para isso.

Eu diria que, quando você começar a adicionar coisas como interfaces ou método a uma enumeração, considere realmente torná-la uma classe. É claro que tenho certeza de que existem casos válidos para fazer enum não tradicionais e, como o limite seria artificial, sou a favor de deixar as pessoas fazerem o que querem lá.

TofuBeer
fonte
4
"... então, na maior parte do tempo, qualquer coisa que você possa fazer com uma classe, você pode fazer com uma enumeração", mas não posso dizer que minha enumeração seja herdada de uma classe abstrata. Então sim, ênfase em "na maior parte".
23613 Adam Parkin
5
Isso ocorre porque todas as enums estendem java.lang.Enum e as classes Java podem se estender apenas de uma classe.
TofuBeer
6

Por exemplo, se você tiver uma enumeração do Logger. Então você deve ter os métodos do logger, como depuração, informações, aviso e erro na interface. Isso torna seu código fracamente acoplado.

Johan
fonte
5

Um dos melhores casos de uso para eu usar enum's com interface são os filtros de predicado. É uma maneira muito elegante de corrigir a falta de tipicidade das coleções apache (se outras bibliotecas não puderem ser usadas).

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
fonte
5

A publicação acima mencionada estratégias não enfatizou o suficiente o que uma implementação leve e agradável do padrão de estratégia usando enumeras faz com que você:

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

Você pode ter todas as suas estratégias em um só lugar sem precisar de uma unidade de compilação separada para cada uma. Você recebe um bom envio dinâmico apenas com:

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

Faz com que o Java seja lido quase como uma linguagem agradável e tipicamente digitada!

Robert Moskal
fonte
3

As enums são como classes Java, elas podem ter construtores, métodos etc. A única coisa que você não pode fazer com elas é new EnumName(). As instâncias são predefinidas na sua declaração enum.

Cosmin Cosmin
fonte
Que tal enum Foo extends SomeOtherClass? Portanto, não é exatamente a mesma coisa que uma aula regular, de fato, bem diferente.
23613 Adam Parkin
@ Adam: Eu diria que as maneiras pelas quais as Enums se assemelham às classes são muito mais numerosas do que as maneiras pelas quais elas diferem das classes. Mas não, eles não são idênticos.
scottb
se você descompilar um enum, verá que é uma classe que estende a enumeração e tem as "constantes" já instanciadas nos campos de construtor / inicialização.
Cosmin Cosmin
3

Outra possibilidade:

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);
Jorge Aranda
fonte
3

Ao criar constantes em um arquivo jar, geralmente é útil permitir que os usuários estendam valores de enumeração. Usamos enumerações para chaves PropertyFile e ficamos paralisadas porque ninguém poderia adicionar novas! Abaixo teria funcionado muito melhor.

Dado:

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

alguém poderia ter um enum no pote:

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

e um usuário pode estendê-lo para adicionar suas próprias cores:

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

Aqui está a minha razão pela qual ...

Eu preenchi uma JavaFX ComboBox com os valores de um Enum. Eu tenho uma interface, identificável (especificando um método: identificar), que permite especificar como qualquer objeto se identifica para o meu aplicativo para fins de pesquisa. Essa interface permite verificar listas de qualquer tipo de objetos (qualquer que seja o campo que o objeto possa usar para identidade) para uma correspondência de identidade.

Gostaria de encontrar uma correspondência para um valor de identidade na minha lista ComboBox. Para usar esse recurso no meu ComboBox que contém os valores de Enum, devo poder implementar a interface Identificável no meu Enum (que, por acaso, é trivial de implementar no caso de um Enum).

scottb
fonte
1

Eu usei um enum interno em uma interface que descreve uma estratégia para manter o controle da instância (cada estratégia é um Singleton) a partir daí.

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

O fato de o enum implementar a estratégia é conveniente para permitir que a classe enum atue como proxy para sua Instância fechada. que também implementa a interface.

é um tipo de strategyEnumProxy: P o código clent se parece com isso:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

Se não implementasse a interface, teria sido:

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