Alasan untuk melakukan ini adalah untuk menguji situasi di mana ada nilai enum yang tidak valid tanpa memperkenalkan nilai enum yang tidak valid di sumber inti.
Archimedes Trajano
Ya contoh kemurnian "linguistik". Saya pikir apa yang diinginkan adalah untuk ide penghematan tenaga kerja "pembukuan" dari satu set bilangan bulat yang bertambah secara otomatis seperti yang ada di C ++ sehingga Anda dapat memulai set baru sebagai perpanjangan dari set lama mulai dari 1+ nilai terakhir dari set sebelumnya, dan jika entri diberi nama mewarisi nama-nama dari "subset umum". Meskipun java enum memiliki beberapa hal bagus tentang itu, ia tidak memiliki bilangan bulat otomatis otomatis tambahan sederhana yang menyatakan bantuan yang disediakan oleh C ++ enum.
peterk
4
Sebenarnya, ketika Anda memperluas enum Anda dengan nilai-nilai baru, Anda membuat bukan subkelas, tetapi superkelas. Anda dapat menggunakan nilai-nilai enum dasar di mana-mana alih-alih enum "extended", tetapi tidak sebaliknya, jadi menurut Prinsip Substitusi Liskov, extended enum adalah superclass dari enum dasar.
Ilya
@Ilya ... ya itu benar. Saya menunjukkan bahwa pertanyaannya memiliki kasus penggunaan dunia nyata. Demi argumen, mempertimbangkan dasar Enum dari: PrimaryColours; masuk akal untuk ingin super- kelas ini ke Enum PrimaryAndPastelColoursdengan menambahkan nama warna baru. Liskov masih merupakan gajah di dalam ruangan. Jadi mengapa tidak mulai dengan basis Enum dari: AllMyColours- Dan kemudian orang mungkin sub- kelas semua warna untuk: PrimaryAndPastelColoursdan kemudian sub- kelas ini ke: PrimaryColours(menjaga hierarki dalam pikiran). Java tidak akan mengizinkan itu juga.
Akan
Jawaban:
451
Tidak, Anda tidak dapat melakukan ini di Jawa. Selain dari hal lain, dmungkin akan menjadi contoh A(mengingat ide normal "meluas"), tetapi pengguna yang hanya tahu tentang Atidak akan mengetahuinya - yang mengalahkan titik enum menjadi set terkenal nilai-nilai.
Jika Anda dapat memberi tahu kami lebih banyak tentang bagaimana Anda ingin menggunakan ini, kami berpotensi menyarankan solusi alternatif.
Semua enums secara implisit memperluas java.lang.Enum. Karena Java tidak mendukung multiple inheritance, enum tidak dapat memperpanjang yang lainnya.
givanse
9
Alasan saya ingin memperpanjang adalah karena saya ingin memiliki kelas dasar yang disebut misalnya IntEnum, yang terlihat seperti ini: stackoverflow.com/questions/1681976/enum-with-int-value-in-java/… . Maka semua enum saya dapat memperpanjangnya ... dalam hal ini hanya mendapat manfaat dari warisan dan dengan demikian saya tidak perlu menduplikasi kode "int-based enum" ini sering. Saya baru ke Jawa dan berasal dari C #, dan saya berharap saya kehilangan sesuatu. Pendapat saya saat ini adalah bahwa enum Jawa adalah sakit dibandingkan dengan C #.
Tyler Collier
30
@Tyler: C # enums hanyalah nama yang dikaitkan dengan angka, tanpa validasi otomatis atau apa pun . IMO enums adalah satu bit dari Java yang sebenarnya lebih baik daripada C #.
Jon Skeet
21
Tidak setuju dengan @JonSkeet di sini. Dalam use case saya, saya ingin memisahkan semua logika jahat dalam enum besar saya, dan menyembunyikan logika itu, dan mendefinisikan enum bersih yang memanjang yang lain yang tersembunyi. Enum dengan banyak logika mengalahkan gagasan memiliki variabel bersih yang dideklarasikan sehingga Anda tidak perlu mendeklarasikan ratusan variabel string statis sehingga kelas dengan 5 enum tidak mendapatkan garis yang tidak terbaca dan terlalu besar. Saya tidak ingin pengembang lain peduli dengan menyalin dan menempelkan kedamaian kode untuk proyek berikutnya dan juga memperpanjang base_enum ... masuk akal bagi saya ...
mmm
43
@ givanse ... tidak setuju dengan Anda tentang ekstensi implisit java.lang.Enum menjadi penyebab non-pewarisan karena setiap kelas di java juga secara implisit mewarisi kelas Obyek namun dapat mewarisi beberapa kelas lain karena kemudian akan datang ke dalam hierarki sebagai Object->A->BgantiObject->A->B extends Object
mickeymoon
317
Enum mewakili enumerasi lengkap dari nilai yang mungkin. Jadi jawaban (tidak membantu) adalah tidak.
Sebagai contoh masalah nyata, ambil hari kerja, hari akhir pekan dan, serikat pekerja, hari dalam seminggu. Kami dapat menetapkan semua hari dalam hari-hari-minggu tetapi kami tidak akan dapat mewakili properti khusus untuk hari kerja dan akhir pekan.
Apa yang bisa kami lakukan adalah memiliki tiga tipe enum dengan pemetaan antara hari kerja / akhir pekan dan hari dalam seminggu.
Apakah tidak ada masalah dengan ini? Pernyataan pergantian tidak akan berfungsi pada antarmuka, tetapi berfungsi pada enum biasa. Tidak bekerja dengan mengganti jenis membunuh salah satu hal yang lebih baik tentang enum.
Manius
9
Saya pikir mungkin ada masalah lain dengan ini. Tidak ada persamaan antara Weekday.MON dan DayOfWeek.MON. Bukankah itu manfaat besar lainnya dari enum? Saya tidak punya solusi yang lebih baik, hanya menyadari ini karena saya sedang berusaha menemukan jawaban terbaik. Kurangnya bisa menggunakan == memaksa tangan sedikit.
Snekse
2
@Crusader ya, itulah tepatnya trade-off. Jika Anda menginginkan sesuatu yang dapat diperluas, Anda tidak dapat memiliki pernyataan sakelar yang diperbaiki, jika Anda menginginkan sekumpulan nilai yang diketahui tetap, Anda secara tautologis tidak dapat memiliki sesuatu yang dapat diperluas.
djechlin
3
Beralih dari enum ke antarmuka, Anda juga kehilangan panggilan statis ke nilai (). Ini membuat refactoring sulit, terutama jika Anda memutuskan untuk memperpanjang enum Anda dan menambahkan antarmuka sebagai penghalang abstraksi ke enum yang sudah mapan.
Joshua Goldberg
4
Pendekatan untuk memperoleh enum dari antarmuka ini digunakan oleh Java 1.7 API, misal java.nio.file.Files.write () menggunakan array OpenOption sebagai argumen terakhir. OpenOption adalah antarmuka, tetapi ketika kita memanggil fungsi ini, kita biasanya melewati konstanta enum StandardOpenOption, yang diturunkan dari OpenOption. Ini memiliki keuntungan karena dapat diperluas, tetapi juga memiliki kelemahan. Implementasinya menderita karena OpenOption adalah sebuah antarmuka. Itu menciptakan HashSet <OpenOption> dari array yang diteruskan, ketika itu bisa membuat EnumSet yang lebih hemat ruang dan waktu. Dan itu tidak bisa menggunakan saklar.
Ini melibatkan pembuatan antarmuka dan menggunakannya di mana Anda saat ini menggunakan enum. Kemudian buat enum mengimplementasikan antarmuka. Anda bisa menambahkan lebih banyak konstanta dengan membuat enum baru itu juga memperluas antarmuka.
Sebaiknya sebutkan penggunaan metode pabrik di antarmuka. Cara yang bagus untuk berbagi fungsionalitas umum antara Enum terkait mengingat perluasan bukan merupakan solusi yang layak.
Tim Clemons
8
Bisakah Anda memberikan rincian lebih lanjut (kode :)) tentang pola ini?
Dherik
3
Pola itu tidak memungkinkan untuk memperpanjang nilai-nilai enum. Yang merupakan titik dalam pertanyaan yang diajukan.
Eria
55
Di bawah selimut ENUM Anda hanyalah kelas reguler yang dihasilkan oleh kompiler. Kelas yang dihasilkan meluas java.lang.Enum. Alasan teknis Anda tidak dapat memperluas kelas yang dihasilkan adalah karena kelas yang dihasilkan adalah final. Alasan konseptual untuk menjadi final dibahas dalam topik ini. Tapi saya akan menambahkan mekanisme untuk diskusi.
Berikut ini adalah tes enum:
publicenum TEST {
ONE, TWO, THREE;}
Kode yang dihasilkan dari javap:
publicfinalclass TEST extends java.lang.Enum<TEST>{publicstaticfinal TEST ONE;publicstaticfinal TEST TWO;publicstaticfinal TEST THREE;static{};publicstatic TEST[] values();publicstatic TEST valueOf(java.lang.String);}
Dapat dibayangkan Anda bisa mengetikkan kelas ini sendiri dan menjatuhkan "final". Tetapi kompiler mencegah Anda untuk memperluas "java.lang.Enum" secara langsung. Anda dapat memutuskan untuk TIDAK memperpanjang java.lang.Enum, tetapi kemudian kelas Anda dan kelas turunannya tidak akan menjadi instance dari java.lang.Enum ... yang mungkin tidak terlalu berarti bagi Anda!
Apa yang dilakukan dengan blok statis kosong? 'statis {};'
soote
1
Tidak ada kode di dalamnya. Program "javap" menunjukkan blok kosong.
ChrisCantrell
Aneh untuk memilikinya di sana jika tidak melakukan apa-apa bukan
soote
4
Kamu benar! Kesalahanku. Ini BUKAN blok kode yang kosong. Jika Anda menjalankan "javap -c" Anda akan melihat kode aktual di dalam blok statis. Blok statis membuat semua instance ENUM (SATU, DUA, dan TIGA di sini). Maaf soal itu.
ChrisCantrell
1
Terima kasih telah menyatakan fakta yang sebenarnya: karena java.lang.Enum dinyatakan final.
Benjamin
26
enum A {a,b,c}enum B extends A {d}/*B is {a,b,c,d}*/
dapat ditulis sebagai:
publicenumAll{
a (ClassGroup.A,ClassGroup.B),
b (ClassGroup.A,ClassGroup.B),
c (ClassGroup.A,ClassGroup.B),
d (ClassGroup.B)...
ClassGroup.B.getMembers () berisi {a, b, c, d}
Bagaimana ini bisa berguna: Katakanlah kita menginginkan sesuatu seperti: Kami memiliki acara dan kami menggunakan enum. Enum tersebut dapat dikelompokkan berdasarkan pemrosesan serupa. Jika kita memiliki operasi dengan banyak elemen, maka beberapa peristiwa mulai beroperasi, beberapa hanya langkah dan yang lain mengakhiri operasi. Untuk mengumpulkan operasi seperti itu dan menghindari kasus sakelar panjang, kami dapat mengelompokkannya seperti dalam contoh dan menggunakan:
Di atas jika kita mengalami beberapa kegagalan (myEvent.is (State_StatusGroup.FAIL)) maka iterasi dengan peristiwa sebelumnya kita dapat dengan mudah memeriksa apakah kita harus mengembalikan transfer uang dengan:
menarik, kita bisa menggunakan juga toumtring () yang sangat enum, dan pada akhirnya membandingkan string; dan untuk menggunakan switch, kita hanya perlu melemparkan objek ke enum yang diketahui; satu-satunya masalah adalah 2 pengembang memperluas dan membuat id enum identik, dan kemudian mencoba untuk menggabungkan kedua kode :), sekarang saya pikir saya mengerti mengapa enum harus tetap tidak dapat diperpanjang.
Kelemahan kecil dari penggunaan antarmuka untuk meniru enum yang dapat dikembangkan adalah implementasi tidak dapat diwarisi dari satu jenis enum ke yang lain. Dalam kasus contoh Operasi kami, logika untuk menyimpan dan mengambil simbol yang terkait dengan operasi diduplikasi dalam BasicOperation dan ExtendedOperation. Dalam hal ini tidak masalah karena sangat sedikit kode yang diduplikasi. Jika ada sejumlah besar fungsi bersama, Anda bisa merangkumnya dalam kelas pembantu atau metode pembantu statis untuk menghilangkan duplikasi kode.
Singkatnya, sementara Anda tidak bisa menulis tipe enum yang bisa diperluas, Anda bisa meniru itu dengan menulis antarmuka untuk pergi dengan tipe enum dasar yang mengimplementasikan antarmuka. Ini memungkinkan klien untuk menulis enum mereka sendiri yang mengimplementasikan antarmuka. Enum ini kemudian dapat digunakan di mana pun tipe enum dasar dapat digunakan, dengan asumsi API ditulis dalam bentuk antarmuka.
Saya cenderung menghindari enum, karena tidak dapat diperpanjang. Untuk tetap menggunakan contoh OP, jika A ada di perpustakaan dan B di kode Anda sendiri, Anda tidak bisa memperpanjang A jika itu adalah enum. Inilah mengapa saya terkadang mengganti enum:
// access like enum: A.apublicclass A {publicstaticfinal A a =new A();publicstaticfinal A b =new A();publicstaticfinal A c =new A();/*
* In case you need to identify your constant
* in different JVMs, you need an id. This is the case if
* your object is transfered between
* different JVM instances (eg. save/load, or network).
* Also, switch statements don't work with
* Objects, but work with int.
*/publicstaticint maxId=0;publicint id = maxId++;publicint getId(){return id;}}publicclass B extends A {/*
* good: you can do like
* A x = getYourEnumFromSomeWhere();
* if(x instanceof B) ...;
* to identify which enum x
* is of.
*/publicstaticfinal A d =new A();}publicclass C extends A {/* Good: e.getId() != d.getId()
* Bad: in different JVMs, C and B
* might be initialized in different order,
* resulting in different IDs.
* Workaround: use a fixed int, or hash code.
*/publicstaticfinal A e =new A();publicint getId(){return-32489132;};}
Ada beberapa lubang yang harus dihindari, lihat komentar dalam kode. Tergantung pada kebutuhan Anda, ini adalah alternatif yang solid dan dapat diperluas untuk enum.
mungkin baik-baik saja jika Anda hanya perlu beberapa peraturan untuk contoh. Tetapi enum juga memiliki nama properti yang cukup berguna.
inor
6
Ini adalah bagaimana saya meningkatkan pola pewarisan enum dengan pemeriksaan runtime di penginisialisasi statis. The BaseKind#checkEnumExtendercek yang "memperpanjang" enum menyatakan semua nilai-nilai dasar enum dengan cara yang persis sama sehingga #name()dan #ordinal()tetap sepenuhnya kompatibel.
Masih ada copy-paste yang terlibat untuk mendeklarasikan nilai tetapi program gagal dengan cepat jika seseorang menambahkan atau memodifikasi nilai di kelas dasar tanpa memperbarui yang diperluas.
Perilaku umum untuk enum yang berbeda saling memperpanjang:
publicinterfaceKind{/**
* Let's say we want some additional member.
*/String description();/**
* Standard {@code Enum} method.
*/String name();/**
* Standard {@code Enum} method.
*/int ordinal();}
Base enum, dengan metode verifikasi:
publicenumBaseKindimplementsKind{
FIRST("First"),
SECOND("Second"),;privatefinalString description ;publicString description(){return description ;}privateBaseKind(finalString description ){this.description = description ;}publicstaticvoid checkEnumExtender(finalKind[] baseValues,finalKind[] extendingValues
){if( extendingValues.length < baseValues.length ){thrownewIncorrectExtensionError("Only "+ extendingValues.length +" values against "+ baseValues.length +" base values");}for(int i =0; i < baseValues.length ; i ++){finalKind baseValue = baseValues[ i ];finalKind extendingValue = extendingValues[ i ];if( baseValue.ordinal()!= extendingValue.ordinal()){thrownewIncorrectExtensionError("Base ordinal "+ baseValue.ordinal()+" doesn't match with "+ extendingValue.ordinal());}if(! baseValue.name().equals( extendingValue.name())){thrownewIncorrectExtensionError("Base name[ "+ i +"] "+ baseValue.name()+" doesn't match with "+ extendingValue.name());}if(! baseValue.description().equals( extendingValue.description())){thrownewIncorrectExtensionError("Description[ "+ i +"] "+ baseValue.description()+" doesn't match with "+ extendingValue.description());}}}publicstaticclassIncorrectExtensionErrorextendsError{publicIncorrectExtensionError(finalString s ){super( s );}}}
@AxelAdvento Idenya di sini adalah kita bergantung pada antarmuka Dayyang memiliki metode valueOf()lalu switch(Day.valueOf()), ini diterapkan oleh WeekDay, WeekEndDayenums.
Khaled Lela
3
Saya sarankan Anda mengambil pendekatan sebaliknya.
Alih-alih memperluas enumerasi yang ada, buat yang lebih besar dan buat subset dari itu. Sebagai contoh jika Anda memiliki enumerasi yang disebut PET dan Anda ingin memperpanjangnya ke HEWAN Anda harus melakukan ini sebagai gantinya:
publicenum ANIMAL {
WOLF,CAT, DOG
}EnumSet<ANIMAL> pets =EnumSet.of(ANIMAL.CAT, ANIMAL.DOG);
Hati-hati, hewan peliharaan bukan koleksi abadi, Anda mungkin ingin menggunakan Guava atau Java9 untuk keamanan lebih.
Setelah memiliki masalah yang sama, saya ingin memposting perspektif saya. Saya pikir ada beberapa faktor pendorong untuk melakukan sesuatu seperti ini:
Anda ingin memiliki beberapa kode enum terkait, tetapi di kelas yang berbeda. Dalam kasus saya, saya memiliki kelas dasar dengan beberapa kode yang didefinisikan dalam enum terkait. Pada beberapa tanggal kemudian (hari ini!) Saya ingin memberikan beberapa fungsionalitas baru ke kelas dasar, yang juga berarti kode baru untuk enum.
Kelas turunan akan mendukung enum kelas dasar maupun miliknya sendiri. Tidak ada nilai duplikat enum! Jadi: bagaimana cara membuat enum untuk subclass yang menyertakan enum dari induknya beserta nilai-nilai barunya.
Menggunakan antarmuka tidak benar-benar memotongnya: Anda dapat secara tidak sengaja mendapatkan nilai duplikat enum. Tidak diinginkan.
Saya akhirnya hanya menggabungkan enum: ini memastikan bahwa tidak ada nilai duplikat, dengan mengorbankan ikatan yang kurang erat dengan kelas terkait. Tapi, saya pikir masalah duplikat adalah perhatian utama saya ...
Sebagai bantuan untuk memahami mengapa memperpanjang Enum tidak masuk akal pada tingkat implementasi bahasa untuk mempertimbangkan apa yang akan terjadi jika Anda melewatkan instance dari Enum yang diperluas ke rutin yang hanya memahami basis Enum. Suatu saklar yang dijanjikan oleh kompiler dengan semua case yang dicakup sebenarnya tidak akan mencakup nilai-nilai Enum yang diperluas.
Ini lebih jauh menekankan bahwa nilai-nilai Java Enum bukan bilangan bulat seperti C, misalnya: untuk menggunakan Java Enum sebagai indeks array, Anda harus secara eksplisit meminta anggota ordinal (), untuk memberikan java Enum nilai integer acak yang harus Anda tambahkan bidang eksplisit untuk itu dan referensi yang bernama anggota.
Ini bukan komentar tentang keinginan OP, hanya tentang mengapa Java tidak akan pernah melakukannya.
Dengan harapan solusi elegan dari kolega saya ini bahkan terlihat di posting panjang ini saya ingin membagikan pendekatan ini untuk subkelas yang mengikuti pendekatan antarmuka dan seterusnya.
Perlu diketahui bahwa kami menggunakan pengecualian khusus di sini dan kode ini tidak dapat dikompilasi kecuali Anda menggantinya dengan pengecualian Anda.
Dokumentasinya luas dan saya harap ini bisa dimengerti oleh sebagian besar dari Anda.
Antarmuka yang harus diimplementasikan setiap enclass subclass.
publicinterfaceParameter{/**
* Retrieve the parameters name.
*
* @return the name of the parameter
*/String getName();/**
* Retrieve the parameters type.
*
* @return the {@link Class} according to the type of the parameter
*/Class<?> getType();/**
* Matches the given string with this parameters value pattern (if applicable). This helps to find
* out if the given string is a syntactically valid candidate for this parameters value.
*
* @param valueStr <i>optional</i> - the string to check for
* @return <code>true</code> in case this parameter has no pattern defined or the given string
* matches the defined one, <code>false</code> in case <code>valueStr</code> is
* <code>null</code> or an existing pattern is not matched
*/boolean match(finalString valueStr);/**
* This method works as {@link #match(String)} but throws an exception if not matched.
*
* @param valueStr <i>optional</i> - the string to check for
* @throws ArgumentException with code
* <dl>
* <dt>PARAM_MISSED</dt>
* <dd>if <code>valueStr</code> is <code>null</code></dd>
* <dt>PARAM_BAD</dt>
* <dd>if pattern is not matched</dd>
* </dl>
*/void matchEx(finalString valueStr)throwsArgumentException;/**
* Parses a value for this parameter from the given string. This method honors the parameters data
* type and potentially other criteria defining a valid value (e.g. a pattern).
*
* @param valueStr <i>optional</i> - the string to parse the parameter value from
* @return the parameter value according to the parameters type (see {@link #getType()}) or
* <code>null</code> in case <code>valueStr</code> was <code>null</code>.
* @throws ArgumentException in case <code>valueStr</code> is not parsable as a value for this
* parameter.
*/Object parse(finalString valueStr)throwsArgumentException;/**
* Converts the given value to its external form as it is accepted by {@link #parse(String)}. For
* most (ordinary) parameters this is simply a call to {@link String#valueOf(Object)}. In case the
* parameter types {@link Object#toString()} method does not return the external form (e.g. for
* enumerations), this method has to be implemented accordingly.
*
* @param value <i>mandatory</i> - the parameters value
* @return the external form of the parameters value, never <code>null</code>
* @throws InternalServiceException in case the given <code>value</code> does not match
* {@link #getType()}
*/String toString(finalObject value)throwsInternalServiceException;}
Kelas dasar penerapan ENUM.
publicenumParametersimplementsParameter{/**
* ANY ENUM VALUE
*/
VALUE(newParameterImpl<String>("VALUE",String.class,"[A-Za-z]{3,10}"));/**
* The parameter wrapped by this enum constant.
*/privateParameter param;/**
* Constructor.
*
* @param param <i>mandatory</i> - the value for {@link #param}
*/privateParameters(finalParameter param){this.param = param;}/**
* {@inheritDoc}
*/@OverridepublicString getName(){returnthis.param.getName();}/**
* {@inheritDoc}
*/@OverridepublicClass<?> getType(){returnthis.param.getType();}/**
* {@inheritDoc}
*/@Overridepublicboolean match(finalString valueStr){returnthis.param.match(valueStr);}/**
* {@inheritDoc}
*/@Overridepublicvoid matchEx(finalString valueStr){this.param.matchEx(valueStr);}/**
* {@inheritDoc}
*/@OverridepublicObject parse(finalString valueStr)throwsArgumentException{returnthis.param.parse(valueStr);}/**
* {@inheritDoc}
*/@OverridepublicString toString(finalObject value)throwsInternalServiceException{returnthis.param.toString(value);}}
ENUM subkelas yang "mewarisi" dari kelas dasar.
publicenumExtendedParametersimplementsParameter{/**
* ANY ENUM VALUE
*/
VALUE(my.package.name.VALUE);/**
* EXTENDED ENUM VALUE
*/
EXTENDED_VALUE(newParameterImpl<String>("EXTENDED_VALUE",String.class,"[0-9A-Za-z_.-]{1,20}"));/**
* The parameter wrapped by this enum constant.
*/privateParameter param;/**
* Constructor.
*
* @param param <i>mandatory</i> - the value for {@link #param}
*/privateParameters(finalParameter param){this.param = param;}/**
* {@inheritDoc}
*/@OverridepublicString getName(){returnthis.param.getName();}/**
* {@inheritDoc}
*/@OverridepublicClass<?> getType(){returnthis.param.getType();}/**
* {@inheritDoc}
*/@Overridepublicboolean match(finalString valueStr){returnthis.param.match(valueStr);}/**
* {@inheritDoc}
*/@Overridepublicvoid matchEx(finalString valueStr){this.param.matchEx(valueStr);}/**
* {@inheritDoc}
*/@OverridepublicObject parse(finalString valueStr)throwsArgumentException{returnthis.param.parse(valueStr);}/**
* {@inheritDoc}
*/@OverridepublicString toString(finalObject value)throwsInternalServiceException{returnthis.param.toString(value);}}
Akhirnya ParameterImpl generik untuk menambahkan beberapa utilitas.
publicclassParameterImpl<T>implementsParameter{/**
* The default pattern for numeric (integer, long) parameters.
*/privatestaticfinalPattern NUMBER_PATTERN =Pattern.compile("[0-9]+");/**
* The default pattern for parameters of type boolean.
*/privatestaticfinalPattern BOOLEAN_PATTERN =Pattern.compile("0|1|true|false");/**
* The name of the parameter, never <code>null</code>.
*/privatefinalString name;/**
* The data type of the parameter.
*/privatefinalClass<T> type;/**
* The validation pattern for the parameters values. This may be <code>null</code>.
*/privatefinalPattern validator;/**
* Shortcut constructor without <code>validatorPattern</code>.
*
* @param name <i>mandatory</i> - the value for {@link #name}
* @param type <i>mandatory</i> - the value for {@link #type}
*/publicParameterImpl(finalString name,finalClass<T> type){this(name, type,null);}/**
* Constructor.
*
* @param name <i>mandatory</i> - the value for {@link #name}
* @param type <i>mandatory</i> - the value for {@link #type}
* @param validatorPattern - <i>optional</i> - the pattern for {@link #validator}
* <dl>
* <dt style="margin-top:0.25cm;"><i>Note:</i>
* <dd>The default validation patterns {@link #NUMBER_PATTERN} or
* {@link #BOOLEAN_PATTERN} are applied accordingly.
* </dl>
*/publicParameterImpl(finalString name,finalClass<T> type,finalString validatorPattern){this.name = name;this.type = type;if(null!= validatorPattern){this.validator =Pattern.compile(validatorPattern);}elseif(Integer.class==this.type ||Long.class==this.type){this.validator = NUMBER_PATTERN;}elseif(Boolean.class==this.type){this.validator = BOOLEAN_PATTERN;}else{this.validator =null;}}/**
* {@inheritDoc}
*/@Overridepublicboolean match(finalString valueStr){if(null== valueStr){returnfalse;}if(null!=this.validator){finalMatcher matcher =this.validator.matcher(valueStr);return matcher.matches();}returntrue;}/**
* {@inheritDoc}
*/@Overridepublicvoid matchEx(finalString valueStr)throwsArgumentException{if(false==this.match(valueStr)){if(null== valueStr){throwArgumentException.createEx(ErrorCode.PARAM_MISSED,"The value must not be null",this.name);}throwArgumentException.createEx(ErrorCode.PARAM_BAD,"The value must match the pattern: "+this.validator.pattern(),this.name);}}/**
* Parse the parameters value from the given string value according to {@link #type}. Additional
* the value is checked by {@link #matchEx(String)}.
*
* @param valueStr <i>optional</i> - the string value to parse the value from
* @return the parsed value, may be <code>null</code>
* @throws ArgumentException in case the parameter:
* <ul>
* <li>does not {@link #matchEx(String)} the {@link #validator}</li>
* <li>cannot be parsed according to {@link #type}</li>
* </ul>
* @throws InternalServiceException in case the type {@link #type} cannot be handled. This is a
* programming error.
*/@Overridepublic T parse(finalString valueStr)throwsArgumentException,InternalServiceException{if(null== valueStr){returnnull;}this.matchEx(valueStr);if(String.class==this.type){returnthis.type.cast(valueStr);}if(Boolean.class==this.type){returnthis.type.cast(Boolean.valueOf(("1".equals(valueStr))||Boolean.valueOf(valueStr)));}try{if(Integer.class==this.type){returnthis.type.cast(Integer.valueOf(valueStr));}if(Long.class==this.type){returnthis.type.cast(Long.valueOf(valueStr));}}catch(finalNumberFormatException e){throwArgumentException.createEx(ErrorCode.PARAM_BAD,"The value cannot be parsed as "+this.type.getSimpleName().toLowerCase()+".",this.name);}returnthis.parseOther(valueStr);}/**
* Field access for {@link #name}.
*
* @return the value of {@link #name}.
*/@OverridepublicString getName(){returnthis.name;}/**
* Field access for {@link #type}.
*
* @return the value of {@link #type}.
*/@OverridepublicClass<T> getType(){returnthis.type;}/**
* {@inheritDoc}
*/@OverridepublicfinalString toString(finalObject value)throwsInternalServiceException{if(false==this.type.isAssignableFrom(value.getClass())){thrownewInternalServiceException(ErrorCode.PANIC,"Parameter.toString(): Bad type of value. Expected {0} but is {1}.",this.type.getName(),
value.getClass().getName());}if(String.class==this.type ||Integer.class==this.type ||Long.class==this.type){returnString.valueOf(value);}if(Boolean.class==this.type){returnBoolean.TRUE.equals(value)?"1":"0";}returnthis.toStringOther(value);}/**
* Parse parameter values of other (non standard types). This method is called by
* {@link #parse(String)} in case {@link #type} is none of the supported standard types (currently
* String, Boolean, Integer and Long). It is intended for extensions.
* <dl>
* <dt style="margin-top:0.25cm;"><i>Note:</i>
* <dd>This default implementation always throws an InternalServiceException.
* </dl>
*
* @param valueStr <i>mandatory</i> - the string value to parse the value from
* @return the parsed value, may be <code>null</code>
* @throws ArgumentException in case the parameter cannot be parsed according to {@link #type}
* @throws InternalServiceException in case the type {@link #type} cannot be handled. This is a
* programming error.
*/protected T parseOther(finalString valueStr)throwsArgumentException,InternalServiceException{thrownewInternalServiceException(ErrorCode.PANIC,"ParameterImpl.parseOther(): Unsupported parameter type: "+this.type.getName());}/**
* Convert the values of other (non standard types) to their external form. This method is called
* by {@link #toString(Object)} in case {@link #type} is none of the supported standard types
* (currently String, Boolean, Integer and Long). It is intended for extensions.
* <dl>
* <dt style="margin-top:0.25cm;"><i>Note:</i>
* <dd>This default implementation always throws an InternalServiceException.
* </dl>
*
* @param value <i>mandatory</i> - the parameters value
* @return the external form of the parameters value, never <code>null</code>
* @throws InternalServiceException in case the given <code>value</code> does not match
* {@link #getClass()}
*/protectedString toStringOther(finalObject value)throwsInternalServiceException{thrownewInternalServiceException(ErrorCode.PANIC,"ParameterImpl.toStringOther(): Unsupported parameter type: "+this.type.getName());}}
// enum A { a, b, c }staticfinalSet<Short> enumA =newLinkedHashSet<>(Arrays.asList(newShort[]{'a','b','c'}));// enum B extends A { d }staticfinalSet<Short> enumB =newLinkedHashSet<>(enumA);static{
enumB.add((short)'d');// If you have to add more elements:// enumB.addAll(Arrays.asList(new Short[]{ 'e', 'f', 'g', '♯', '♭' }));}
LinkedHashSetmenyatakan bahwa setiap entri hanya ada satu kali, dan urutannya dipertahankan. Jika pesanan tidak masalah, Anda bisa menggunakannya HashSet. Kode berikut tidak dimungkinkan di Java:
for(A a : B.values()){// enum B extends A { d }switch(a){case a:case b:case c:System.out.println("Value is: "+ a.toString());break;default:thrownewIllegalStateException("This should never happen.");}}
Kode dapat ditulis sebagai berikut:
for(Short a : enumB){switch(a){case'a':case'b':case'c':System.out.println("Value is: "+newString(Character.toChars(a)));break;default:thrownewIllegalStateException("This should never happen.");}}
Dari Java 7 dan seterusnya Anda bahkan dapat melakukan hal yang sama dengan String:
// enum A { BACKWARDS, FOREWARDS, STANDING }staticfinalSet<String> enumA =newLinkedHashSet<>(Arrays.asList(newString[]{"BACKWARDS","FOREWARDS","STANDING"}));// enum B extends A { JUMP }staticfinalSet<String> enumB =newLinkedHashSet<>(enumA);static{
enumB.add("JUMP");}
Menggunakan penggantian enum:
for(String a : enumB){switch(a){case"BACKWARDS":case"FOREWARDS":case"STANDING":System.out.println("Value is: "+ a);break;default:thrownewIllegalStateException("This should never happen.");}}
PrimaryColours
; masuk akal untuk ingin super- kelas ini ke EnumPrimaryAndPastelColours
dengan menambahkan nama warna baru. Liskov masih merupakan gajah di dalam ruangan. Jadi mengapa tidak mulai dengan basis Enum dari:AllMyColours
- Dan kemudian orang mungkin sub- kelas semua warna untuk:PrimaryAndPastelColours
dan kemudian sub- kelas ini ke:PrimaryColours
(menjaga hierarki dalam pikiran). Java tidak akan mengizinkan itu juga.Jawaban:
Tidak, Anda tidak dapat melakukan ini di Jawa. Selain dari hal lain,
d
mungkin akan menjadi contohA
(mengingat ide normal "meluas"), tetapi pengguna yang hanya tahu tentangA
tidak akan mengetahuinya - yang mengalahkan titik enum menjadi set terkenal nilai-nilai.Jika Anda dapat memberi tahu kami lebih banyak tentang bagaimana Anda ingin menggunakan ini, kami berpotensi menyarankan solusi alternatif.
sumber
Object->A->B
gantiObject->A->B extends Object
Enum mewakili enumerasi lengkap dari nilai yang mungkin. Jadi jawaban (tidak membantu) adalah tidak.
Sebagai contoh masalah nyata, ambil hari kerja, hari akhir pekan dan, serikat pekerja, hari dalam seminggu. Kami dapat menetapkan semua hari dalam hari-hari-minggu tetapi kami tidak akan dapat mewakili properti khusus untuk hari kerja dan akhir pekan.
Apa yang bisa kami lakukan adalah memiliki tiga tipe enum dengan pemetaan antara hari kerja / akhir pekan dan hari dalam seminggu.
Atau, kita bisa memiliki antarmuka terbuka untuk hari-minggu:
Atau kita bisa menggabungkan dua pendekatan:
sumber
Solusi yang disarankan untuk ini adalah pola enum yang dapat diperluas .
Ini melibatkan pembuatan antarmuka dan menggunakannya di mana Anda saat ini menggunakan enum. Kemudian buat enum mengimplementasikan antarmuka. Anda bisa menambahkan lebih banyak konstanta dengan membuat enum baru itu juga memperluas antarmuka.
sumber
Di bawah selimut ENUM Anda hanyalah kelas reguler yang dihasilkan oleh kompiler. Kelas yang dihasilkan meluas
java.lang.Enum
. Alasan teknis Anda tidak dapat memperluas kelas yang dihasilkan adalah karena kelas yang dihasilkan adalahfinal
. Alasan konseptual untuk menjadi final dibahas dalam topik ini. Tapi saya akan menambahkan mekanisme untuk diskusi.Berikut ini adalah tes enum:
Kode yang dihasilkan dari javap:
Dapat dibayangkan Anda bisa mengetikkan kelas ini sendiri dan menjatuhkan "final". Tetapi kompiler mencegah Anda untuk memperluas "java.lang.Enum" secara langsung. Anda dapat memutuskan untuk TIDAK memperpanjang java.lang.Enum, tetapi kemudian kelas Anda dan kelas turunannya tidak akan menjadi instance dari java.lang.Enum ... yang mungkin tidak terlalu berarti bagi Anda!
sumber
dapat ditulis sebagai:
Bagaimana ini bisa berguna: Katakanlah kita menginginkan sesuatu seperti: Kami memiliki acara dan kami menggunakan enum. Enum tersebut dapat dikelompokkan berdasarkan pemrosesan serupa. Jika kita memiliki operasi dengan banyak elemen, maka beberapa peristiwa mulai beroperasi, beberapa hanya langkah dan yang lain mengakhiri operasi. Untuk mengumpulkan operasi seperti itu dan menghindari kasus sakelar panjang, kami dapat mengelompokkannya seperti dalam contoh dan menggunakan:
Contoh:
Tambahkan beberapa yang lebih canggih:
Di atas jika kita mengalami beberapa kegagalan (myEvent.is (State_StatusGroup.FAIL)) maka iterasi dengan peristiwa sebelumnya kita dapat dengan mudah memeriksa apakah kita harus mengembalikan transfer uang dengan:
Ini dapat bermanfaat untuk:
sumber
Berikut adalah cara bagaimana saya menemukan cara memperluas enum ke enum lain, adalah pendekatan yang sangat ketat:
Misalkan Anda memiliki enum dengan konstanta yang sama:
maka Anda dapat mencoba melakukan manual meluas dengan cara ini:
tentu saja setiap kali Anda perlu memperluas konstanta Anda harus memodifikasi file SubEnum Anda.
sumber
Jika Anda melewatkannya, ada bab dalam buku Joshua Bloch yang sangat bagus " Java Effective, 2nd edition ".
Ekstrak di sini .
Hanya kesimpulannya:
sumber
Saya cenderung menghindari enum, karena tidak dapat diperpanjang. Untuk tetap menggunakan contoh OP, jika A ada di perpustakaan dan B di kode Anda sendiri, Anda tidak bisa memperpanjang A jika itu adalah enum. Inilah mengapa saya terkadang mengganti enum:
Ada beberapa lubang yang harus dihindari, lihat komentar dalam kode. Tergantung pada kebutuhan Anda, ini adalah alternatif yang solid dan dapat diperluas untuk enum.
sumber
Ini adalah bagaimana saya meningkatkan pola pewarisan enum dengan pemeriksaan runtime di penginisialisasi statis. The
BaseKind#checkEnumExtender
cek yang "memperpanjang" enum menyatakan semua nilai-nilai dasar enum dengan cara yang persis sama sehingga#name()
dan#ordinal()
tetap sepenuhnya kompatibel.Masih ada copy-paste yang terlibat untuk mendeklarasikan nilai tetapi program gagal dengan cepat jika seseorang menambahkan atau memodifikasi nilai di kelas dasar tanpa memperbarui yang diperluas.
Perilaku umum untuk enum yang berbeda saling memperpanjang:
Base enum, dengan metode verifikasi:
Sampel ekstensi:
sumber
Berdasarkan @Tom Hawtin - jawaban tackline kami tambahkan dukungan sakelar,
sumber
valueOf()
metode ini?Day
yang memiliki metodevalueOf()
laluswitch(Day.valueOf())
, ini diterapkan olehWeekDay, WeekEndDay
enums.Saya sarankan Anda mengambil pendekatan sebaliknya.
Alih-alih memperluas enumerasi yang ada, buat yang lebih besar dan buat subset dari itu. Sebagai contoh jika Anda memiliki enumerasi yang disebut PET dan Anda ingin memperpanjangnya ke HEWAN Anda harus melakukan ini sebagai gantinya:
Hati-hati, hewan peliharaan bukan koleksi abadi, Anda mungkin ingin menggunakan Guava atau Java9 untuk keamanan lebih.
sumber
Setelah memiliki masalah yang sama, saya ingin memposting perspektif saya. Saya pikir ada beberapa faktor pendorong untuk melakukan sesuatu seperti ini:
Menggunakan antarmuka tidak benar-benar memotongnya: Anda dapat secara tidak sengaja mendapatkan nilai duplikat enum. Tidak diinginkan.
Saya akhirnya hanya menggabungkan enum: ini memastikan bahwa tidak ada nilai duplikat, dengan mengorbankan ikatan yang kurang erat dengan kelas terkait. Tapi, saya pikir masalah duplikat adalah perhatian utama saya ...
sumber
Sebagai bantuan untuk memahami mengapa memperpanjang Enum tidak masuk akal pada tingkat implementasi bahasa untuk mempertimbangkan apa yang akan terjadi jika Anda melewatkan instance dari Enum yang diperluas ke rutin yang hanya memahami basis Enum. Suatu saklar yang dijanjikan oleh kompiler dengan semua case yang dicakup sebenarnya tidak akan mencakup nilai-nilai Enum yang diperluas.
Ini lebih jauh menekankan bahwa nilai-nilai Java Enum bukan bilangan bulat seperti C, misalnya: untuk menggunakan Java Enum sebagai indeks array, Anda harus secara eksplisit meminta anggota ordinal (), untuk memberikan java Enum nilai integer acak yang harus Anda tambahkan bidang eksplisit untuk itu dan referensi yang bernama anggota.
Ini bukan komentar tentang keinginan OP, hanya tentang mengapa Java tidak akan pernah melakukannya.
sumber
Dengan harapan solusi elegan dari kolega saya ini bahkan terlihat di posting panjang ini saya ingin membagikan pendekatan ini untuk subkelas yang mengikuti pendekatan antarmuka dan seterusnya.
Perlu diketahui bahwa kami menggunakan pengecualian khusus di sini dan kode ini tidak dapat dikompilasi kecuali Anda menggantinya dengan pengecualian Anda.
Dokumentasinya luas dan saya harap ini bisa dimengerti oleh sebagian besar dari Anda.
Antarmuka yang harus diimplementasikan setiap enclass subclass.
Kelas dasar penerapan ENUM.
ENUM subkelas yang "mewarisi" dari kelas dasar.
Akhirnya ParameterImpl generik untuk menambahkan beberapa utilitas.
sumber
Cara saya mengode itu adalah sebagai berikut:
LinkedHashSet
menyatakan bahwa setiap entri hanya ada satu kali, dan urutannya dipertahankan. Jika pesanan tidak masalah, Anda bisa menggunakannyaHashSet
. Kode berikut tidak dimungkinkan di Java:Kode dapat ditulis sebagai berikut:
Dari Java 7 dan seterusnya Anda bahkan dapat melakukan hal yang sama dengan
String
:Menggunakan penggantian enum:
sumber