Mengapa Enum mengimplementasikan Interface?

Jawaban:

130

Enum tidak hanya harus mewakili set pasif (misalnya warna). Mereka dapat mewakili objek yang lebih kompleks dengan fungsionalitas, dan karenanya Anda mungkin ingin menambahkan fungsionalitas lebih lanjut untuk ini - misalnya Anda mungkin memiliki antarmuka seperti Printable, Reportabledll. Dan komponen yang mendukung ini.

Brian Agnew
sumber
259

Berikut adalah satu contoh (yang serupa / lebih baik ditemukan di Java Edisi 2 Efektif):

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

Sekarang untuk mendapatkan daftar Operator Sederhana + Kompleks:

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

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

Jadi di sini Anda menggunakan antarmuka untuk mensimulasikan enum yang dapat diperluas (yang tidak mungkin dilakukan tanpa menggunakan antarmuka).

metode helpermet
sumber
3
Operator kompleks bisa menjadi "pow" dan sejenisnya.
Pimgd
2
Pertama kali melihat sintaks Enum (dengan kurung kurawal setelah anggota), dan saya sudah pemrograman dengan Java selama 6 tahun. TIL.
jrahhali
23

The Comparablecontoh yang diberikan oleh beberapa orang di sini yang salah, karena Enumsudah mengimplementasikan itu. Anda bahkan tidak bisa menimpanya.

Contoh yang lebih baik adalah memiliki antarmuka yang mendefinisikan, katakanlah, tipe data. Anda dapat memiliki enum untuk mengimplementasikan tipe sederhana, dan memiliki kelas normal untuk mengimplementasikan tipe rumit:

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
sumber
2
Ya. Aneh !!. Enum sudah mengimplementasikan antarmuka Sebanding dan tidak mengizinkan untuk mengesampingkan. Saya mencari kode sumber java untuk kelas enum dan tidak dapat menemukan implementasi compareTo. Adakah yang bisa memberi tahu saya apa yang akan dibandingkan di dalam metode compareTo dan ENUM?
AKh
2
@Apakah itu membandingkan ordinals, berarti pemesanan alami adalah urutan konstanta enum dalam file sumber.
Jorn
Enum vars bersifat final dan jadi bandingkan terjadi dengan ==, bukan?
djangofan
@ Djangofan ya.
Jorn
17

Ada kasus yang sering saya gunakan. Saya memiliki IdUtilkelas dengan metode statis untuk bekerja dengan objek yang mengimplementasikan Identifiableantarmuka yang sangat sederhana :

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

Jika saya membuat 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;
        }

    }

Maka saya bisa mendapatkannya dengan idcara ini:

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

Karena Enums dapat mengimplementasikan antarmuka, mereka dapat digunakan untuk menegakkan secara ketat pola tunggal. Mencoba membuat kelas standar yang tunggal memungkinkan ...

  • untuk kemungkinan menggunakan teknik refleksi untuk mengekspos metode pribadi sebagai publik
  • untuk mewarisi dari singleton Anda dan mengganti metode singleton Anda dengan sesuatu yang lain

Enum sebagai lajang membantu mencegah masalah keamanan ini. Ini mungkin salah satu alasan yang berkontribusi untuk membiarkan Enums bertindak sebagai kelas dan mengimplementasikan antarmuka. Tebakan saja.

Lihat /programming/427902/java-enum-singleton dan kelas Singleton di java untuk diskusi lebih lanjut.

Tansir1
sumber
1
for inheriting from your singleton and overriding your singleton's methods with something else. Anda bisa menggunakan a final classuntuk mencegahnya
Dylanthepiguy
10

Diperlukan untuk ekstensibilitas - jika seseorang menggunakan API yang Anda kembangkan, enum yang Anda tetapkan bersifat statis; mereka tidak dapat ditambahkan atau dimodifikasi. Namun, jika Anda membiarkannya mengimplementasikan antarmuka, orang yang menggunakan API dapat mengembangkan enum mereka sendiri menggunakan antarmuka yang sama. Anda kemudian dapat mendaftarkan enum ini dengan manajer enum yang menggabungkan konglomerat dengan antarmuka standar.

Sunting: @Helper Method memiliki contoh sempurna untuk ini. Pikirkan tentang membuat perpustakaan lain mendefinisikan operator baru dan kemudian memberi tahu kelas manajer bahwa 'hei, enum ini ada - daftarkan itu'. Jika tidak, Anda hanya dapat mendefinisikan Operator dalam kode Anda sendiri - tidak akan ada perpanjangan.

Chris Dennett
sumber
7

Enum hanyalah kelas yang menyamar, jadi sebagian besar, apa pun yang dapat Anda lakukan dengan kelas yang dapat Anda lakukan dengan enum.

Saya tidak dapat memikirkan alasan mengapa enum tidak dapat mengimplementasikan antarmuka, pada saat yang sama saya juga tidak dapat memikirkan alasan yang baik bagi mereka untuk melakukannya.

Saya akan mengatakan begitu Anda mulai menambahkan hal-hal seperti antarmuka, atau metode untuk enum Anda harus benar-benar mempertimbangkan untuk menjadikannya kelas. Tentu saja saya yakin ada kasus yang valid untuk melakukan hal-hal non-tradisional enum, dan karena batasnya adalah buatan, saya mendukung membiarkan orang melakukan apa yang mereka inginkan di sana.

TofuBeer
sumber
4
"... jadi untuk sebagian besar hal yang dapat kamu lakukan dengan sebuah kelas dapat kamu lakukan dengan sebuah enum", namun aku tidak bisa mengatakan memiliki enum ku mewarisi dari kelas abstrak. Jadi ya, penekanan pada "sebagian besar".
Adam Parkin
5
Itu karena enum semua memperpanjang java.lang.Enum, dan kelas Java hanya dapat diperluas dari satu kelas.
TofuBeer
6

Misalnya jika Anda memiliki enum Logger. Maka Anda harus memiliki metode logger seperti debug, info, peringatan dan kesalahan di antarmuka. Itu membuat kode Anda longgar digabungkan.

Johan
sumber
6

Penggunaan paling umum untuk ini adalah untuk menggabungkan nilai-nilai dari dua enum ke dalam satu kelompok dan memperlakukan mereka dengan cara yang sama. Misalnya, lihat cara bergabung dengan Buah dan Sayuran .

husayt
sumber
5

Salah satu kasus penggunaan terbaik bagi saya untuk menggunakan enum dengan antarmuka adalah filter Predicate. Ini adalah cara yang sangat elegan untuk memperbaiki kekurangan tipenya dari koleksi apache (Jika perpustakaan lain tidak dapat digunakan).

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

Posting di atas bahwa strategi yang disebutkan tidak cukup menekankan betapa penerapan pola strategi menggunakan enum yang ringan membuat Anda:

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

Anda dapat memiliki semua strategi di satu tempat tanpa memerlukan unit kompilasi terpisah untuk masing-masing. Anda mendapatkan pengiriman dinamis yang bagus hanya dengan:

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

Membuat java membaca hampir seperti bahasa yang diketik longgar yang bagus!

Robert Moskal
sumber
3

Enum seperti Kelas Java, mereka dapat memiliki Konstruktor, Metode, dll. Satu-satunya hal yang tidak dapat Anda lakukan dengannya adalah new EnumName(). Mesin virtual sudah ditentukan sebelumnya dalam deklarasi enum Anda.

Cosmin Cosmin
sumber
Bagaimana dengan enum Foo extends SomeOtherClass? Jadi tidak sama dengan kelas reguler, pada kenyataannya, sangat berbeda.
Adam Parkin
@ Adam: Saya akan mengatakan bahwa cara yang mirip dengan Enums kelas jauh lebih banyak daripada cara di mana mereka berbeda dari kelas. Tapi, tidak, mereka tidak identik.
scottb
jika Anda mendekompilasi enum Anda akan melihat itu adalah kelas yang meluas Pencacahan dan memiliki "konstanta" yang sudah dipakai di bidang konstruktor / inisialisasi.
Cosmin Cosmin
3

Kemungkinan lain:

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

dan menggunakan:

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

Saat membuat konstanta dalam file jar, sering kali bermanfaat untuk membiarkan pengguna memperluas nilai enum. Kami menggunakan enum untuk kunci PropertyFile dan macet karena tidak ada yang bisa menambahkan yang baru! Di bawah ini akan bekerja lebih baik.

Diberikan:

public interface Color {
  String fetchName();
}

dan:

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

seseorang dapat memiliki satu enum di dalam toples:

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

dan pengguna dapat memperluasnya untuk menambahkan warnanya sendiri:

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

Inilah alasan saya mengapa ...

Saya telah mengisi JavaFX ComboBox dengan nilai-nilai Enum. Saya memiliki antarmuka, Dapat Diidentifikasi (menetapkan satu metode: mengidentifikasi), yang memungkinkan saya untuk menentukan bagaimana objek mengidentifikasi dirinya ke aplikasi saya untuk tujuan pencarian. Antarmuka ini memungkinkan saya untuk memindai daftar semua jenis objek (bidang mana pun yang digunakan objek untuk identitas) untuk pencocokan identitas.

Saya ingin menemukan kecocokan untuk nilai identitas di daftar ComboBox saya. Untuk menggunakan kemampuan ini pada ComboBox saya yang berisi nilai-nilai Enum, saya harus dapat mengimplementasikan antarmuka yang dapat diidentifikasi dalam Enum saya (yang, jika terjadi, sepele untuk diimplementasikan dalam kasus Enum).

scottb
sumber
1

Saya menggunakan enum dalam antarmuka yang menggambarkan strategi untuk menjaga kontrol instance (masing-masing strategi adalah Singleton) dari sana.

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

Fakta bahwa enum mengimplementasikan strategi adalah mudah untuk memungkinkan kelas enum untuk bertindak sebagai proksi untuk Instance terlampir. yang juga mengimplementasikan antarmuka.

itu adalah semacam strategyEnumProxy: P kode clent terlihat seperti ini:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

Jika itu tidak mengimplementasikan antarmuka itu sudah:

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