Bir Enum neden bir Arayüz uygular?


Yanıtlar:


130

Numaralamalar sadece pasif kümeleri (ör. Renkleri) temsil etmek zorunda değildir. İşlevselliği olan daha karmaşık nesneleri temsil edebilirler ve bu nedenle bunlara daha fazla işlevsellik eklemek isteyeceksiniz - örneğin Printable, Reportablevb. Gibi arayüzlere ve bunları destekleyen bileşenlere sahip olabilirsiniz.


259

İşte bir örnek (Effective Java 2nd Edition'da benzer / daha iyi bir örnek bulunur):

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

Şimdi hem Basit + Karmaşık Operatörlerin bir listesini almak için:

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

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

Yani burada genişletilebilir numaralandırmaları simüle etmek için bir arayüz kullanıyorsunuz (arayüz kullanmadan mümkün olmaz).


3
Karmaşık operatörler "pow" ve benzerleri olabilir.
Pimgd

2
İlk kez Enum sözdizimini (üyelerden sonra gelen kıvırcık ayraçlarla) görüyorum ve 6 yıldır Java ile programlıyorum. TIL.
jrahhali

23

ComparableBirkaç kişi tarafından verilen örnek burada beri yanlış olduğunu Enumzaten uygular söyledi. Bunu bile geçersiz kılamazsınız.

Daha iyi bir örnek, bir veri türünü tanımlayan bir arayüze sahip olmaktır. Basit türleri uygulamak için bir numaralandırma yapabilir ve karmaşık türleri uygulamak için normal sınıflara sahip olabilirsiniz:

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
Evet, tuhaf !!. Enum, Karşılaştırılabilir arabirimi zaten uygular ve geçersiz kılmaya izin vermez. Java sınıf kodu numaralandırma sınıfı için baktım ve CompareTo uygulama bulamadı. Herkes bana bir CompareTo yöntemi bir ENUM içinde neyin karşılaştırıldığını söyleyebilir?
Akh

2
@AKh ordinalleri karşılaştırır, yani doğal sıralama enum sabitlerinin kaynak dosyadaki sırasıdır.
2012'de

Enum değişkenleri kesindir ve karşılaştırma == ile gerçekleşir, değil mi?
djangofan

@djangofan evet.
Jorn

18

Sık kullandığım bir durum var. IdUtilÇok basit bir Identifiablearayüz uygulamak nesneleri ile çalışmak için statik yöntemlerle bir sınıf var :

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

Bir oluşturursam 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;
        }

    }

O zaman idbu şekilde alabilirim:

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

Tam olarak ihtiyacım olan şey!
Petronella

13

Numaralandırmalar arabirimleri uygulayabildiğinden, tekil kalıbın sıkı bir şekilde uygulanması için kullanılabilirler. Standart bir sınıfı bir singleton yapmaya çalışmak ...

  • özel yöntemleri kamuya açık hale getirmek için yansıtma tekniklerini kullanma olasılığı
  • singleton'unuzdan miras almak ve singletonunuzun yöntemlerini başka bir şeyle geçersiz kılmak için

Tek tonlu numaralandırmalar bu güvenlik sorunlarını önlemeye yardımcı olur. Bu, Enums'ın sınıf olarak hareket etmesine ve arabirimleri uygulamasına izin vermenin katkıda bulunan nedenlerinden biri olabilir. Sadece bir tahmin.

Daha fazla tartışma için /programming/427902/java-enum-singleton ve Java'daki Singleton sınıfına bakın .


1
for inheriting from your singleton and overriding your singleton's methods with something else. Bunu final classönlemek için sadece bir kullanabilirsiniz
Dylanthepiguy

10

Genişletilebilirlik için gereklidir - birisi geliştirdiğiniz bir API kullanıyorsa, tanımladığınız numaralandırmalar statiktir; eklenemez veya değiştirilemez. Ancak, bir arabirim uygulamasına izin verirseniz, API kullanan kişi aynı arabirimi kullanarak kendi numaralandırmasını geliştirebilir. Daha sonra bu numaralandırmayı, standart arayüzle birlikte numaralandıran bir numaralandırma yöneticisi ile kaydedebilirsiniz.

Edit: @Helper Yöntemi bunun mükemmel bir örneğine sahiptir. Diğer kütüphanelerin yeni operatörleri tanımlamasını ve ardından bir yönetici sınıfına 'hey, bu numaralandırma var - kaydettir' demesini söyleyin. Aksi takdirde, Operatörleri yalnızca kendi kodunuzla tanımlayabilirsiniz - genişletilebilirlik olmazdı.


7

Numaralandırmalar sadece kılık değiştirmiş sınıflardır, bu yüzden çoğunlukla, bir enum ile yapabileceğiniz bir sınıfla yapabileceğiniz her şey.

Bir enum'un bir arabirim uygulayamaması için bir neden düşünemiyorum, aynı zamanda onlar için de iyi bir neden düşünemiyorum.

Bir numaralandırmaya arabirimler veya yöntem gibi bir şey eklemeye başladığınızda, bunun yerine bir sınıf yapmayı düşünmelisiniz. Elbette geleneksel olmayan numaralandırma işleri yapmak için geçerli durumlar olduğundan eminim ve sınır yapay bir tane olacağından, insanların orada istediklerini yapmalarına izin vermekten yanayım.


4
“... bu yüzden çoğunlukla bir sınıfla yapabileceğiniz her şeyi bir enum ile yapabilirsiniz”, ancak benim enum'umun soyut bir sınıftan miras almasını söyleyemem. Yani evet, "çoğunlukla" vurgu.
Adam Parkin

5
Çünkü numaralandırmaların tümü java.lang.Enum'u genişletir ve Java sınıfları yalnızca bir sınıftan genişletilebilir.
TofuBeer

6

Örneğin, bir Logger enum'unuz varsa. Sonra arayüzde hata ayıklama, bilgi, uyarı ve hata gibi logger yöntemleri olmalıdır. Kodunuzu gevşek bir şekilde birleştirir.



5

Enum'ları arayüzle kullanmamın en iyi kullanım durumlarından biri Predicate filtreleridir. Apache koleksiyonlarının yazım eksikliğini gidermek için çok zarif bir yol (Diğer kütüphaneler kullanılamıyorsa).

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

Bahsedilen stratejilerin yukarısındaki yazı, enums kullanarak strateji modelinin güzel bir hafif uygulamasının size ne kazandırdığını yeterince vurgulamamıştır:

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

Her biri için ayrı bir derleme birimine ihtiyaç duymadan tüm stratejilerinizi tek bir yerde toplayabilirsiniz. Sadece aşağıdakilerle hoş bir dinamik gönderim alırsınız:

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

Java'yı neredeyse güzel gevşek bir şekilde yazılmış bir dil gibi okur!


3

Numaralandırmalar Java Sınıfları gibidir, Yapıcılara, Yöntemlere vb. Sahip olabilirler new EnumName(). Onlarla yapamayacağınız tek şey . Örnekler, numaralandırma bildiriminizde önceden tanımlanmıştır.


Ne olmuş enum Foo extends SomeOtherClass? Yani normal bir sınıfla aynı şey değil, aslında oldukça farklı.
Adam Parkin

@Adam: Enumların sınıflara benzeme yollarının, sınıflardan farklı olma yollarından çok daha fazla olduğunu söyleyebilirim. Ama hayır, aynı değiller.
scottb

bir numaralandırmayı kodalıyorsanız, bunun Numaralandırma'yı genişleten ve zaten yapıcı / başlatma alanlarında "sabitleri" olan bir sınıf göreceksiniz.
Cosmin Cosmin

3

Başka bir olasılık:

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

ve kullanma:

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

3

Bir jar dosyasında sabitler oluştururken, kullanıcıların enum değerlerini genişletmesine izin vermek genellikle yararlı olur. PropertyFile anahtarları için enumlar kullandık ve takıldık çünkü kimse yenilerini ekleyemedi! Aşağıda çok daha iyi çalışırdı.

Verilen:

public interface Color {
  String fetchName();
}

ve:

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

kavanozda bir numaralandırma olabilir:

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

ve bir kullanıcı kendi renklerini eklemek için genişletebilir:

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

2

İşte nedenim ...

Bir Enum değerleri ile bir JavaFX ComboBox doldurdum. Herhangi bir nesnenin arama amaçları için kendini benim uygulama için nasıl tanımlayacağını belirtmek sağlayan bir arabirim, tanımlanabilir (bir yöntem belirleme: tanımlamak) var. Bu arabirim, bir kimlik eşleşmesi için herhangi bir nesne türünün (nesnenin kimlik için hangi alanı kullanabilirse) listelerini taramamı sağlar.

ComboBox listemde kimlik değeri için bir eşleşme bulmak istiyorum. Bu özelliği, Enum değerlerini içeren ComboBox'ımda kullanmak için, Enum'umdaki Tanımlanabilir arabirimi uygulayabilmem gerekir (ki bu durum bir Enum durumunda uygulanması önemsizdir).


1

Örnek kontrolünü (her strateji bir Singleton'dur) oradan tutmak için bir stratejiyi tanımlayan bir arayüzde bir iç numaralandırma kullandım.

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

Enum'un stratejiyi uygulaması gerçeği, enum sınıfının ekteki Örneği için proxy olarak hareket etmesine izin vermek için uygundur. bu da arayüzü uygular.

bir tür stratejiEnumProxy: P clent kodu şuna benzer:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

Arayüzü uygulamadıysa:

VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.