Jawaban:
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.
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).
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
}
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);
Karena Enums dapat mengimplementasikan antarmuka, mereka dapat digunakan untuk menegakkan secara ketat pola tunggal. Mencoba membuat kelas standar yang tunggal memungkinkan ...
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.
for inheriting from your singleton and overriding your singleton's methods with something else. Anda bisa menggunakan a final classuntuk mencegahnya
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.
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.
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 .
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);
}
}
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!
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.
enum Foo extends SomeOtherClass? Jadi tidak sama dengan kelas reguler, pada kenyataannya, sangat berbeda.
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);
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();
}
}
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).
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);