Enum Jawa sangat bagus. Begitu juga obat generik. Tentu saja kita semua tahu keterbatasan dari yang terakhir karena jenis penghapusan. Tetapi ada satu hal yang saya tidak mengerti, Mengapa saya tidak bisa membuat enum seperti ini:
public enum MyEnum<T> {
LITERAL1<String>,
LITERAL2<Integer>,
LITERAL3<Object>;
}
Parameter tipe generik ini <T>
pada gilirannya dapat berguna di berbagai tempat. Bayangkan parameter tipe umum ke suatu metode:
public <T> T getValue(MyEnum<T> param);
Atau bahkan di kelas enum itu sendiri:
public T convert(Object o);
Contoh lebih konkret # 1
Karena contoh di atas mungkin tampak terlalu abstrak bagi sebagian orang, inilah contoh kehidupan nyata mengapa saya ingin melakukan ini. Dalam contoh ini saya ingin menggunakan
- Enum, karena dengan begitu saya dapat menyebutkan satu set kunci properti yang terbatas
- Generik, karena dengan begitu saya dapat memiliki metode-level-safety untuk menyimpan properti
public interface MyProperties {
public <T> void put(MyEnum<T> key, T value);
public <T> T get(MyEnum<T> key);
}
Contoh lebih konkret # 2
Saya memiliki enumerasi tipe data:
public interface DataType<T> {}
public enum SQLDataType<T> implements DataType<T> {
TINYINT<Byte>,
SMALLINT<Short>,
INT<Integer>,
BIGINT<Long>,
CLOB<String>,
VARCHAR<String>,
...
}
Setiap enum literal jelas akan memiliki properti tambahan berdasarkan tipe generik <T>
, sementara pada saat yang sama, menjadi enum (tidak berubah, tunggal, enumerable, dll.)
Pertanyaan:
Apakah tidak ada yang memikirkan hal ini? Apakah ini keterbatasan terkait kompiler? Mempertimbangkan fakta, bahwa kata kunci " enum " diimplementasikan sebagai gula sintaksis, mewakili kode yang dihasilkan ke JVM, saya tidak mengerti batasan ini.
Siapa yang bisa menjelaskan hal ini kepada saya? Sebelum Anda menjawab, pertimbangkan ini:
- Saya tahu jenis generik dihapus :-)
- Saya tahu ada solusi menggunakan objek Class. Mereka sedang mencari solusi.
- Jenis generik menghasilkan cast yang dihasilkan oleh kompiler dimanapun berlaku (misalnya saat memanggil metode convert ()
- Jenis generik <T> akan berada di enum. Karena itu ia diikat oleh masing-masing literal enum. Oleh karena itu kompiler akan tahu, tipe apa yang diterapkan ketika menulis sesuatu seperti
String string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);
- Hal yang sama berlaku untuk parameter tipe generik dalam
T getvalue()
metode ini. Kompiler dapat menerapkan tipe casting saat memanggilString string = someClass.getValue(LITERAL1)
enum
ubah idiom menjadi "typesafe enum" yang kami gunakan sebelum Java 1.5. Tiba-tiba, Anda dapat membuat parameter anggota enum Anda. Mungkin itulah yang akan saya lakukan sekarang.Jawaban:
Ini sekarang sedang dibahas pada JEP-301 Enhanced Enums . Contoh yang diberikan dalam JEP adalah, yang persis apa yang saya cari:
Sayangnya, JEP masih berjuang dengan masalah signifikan: http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html
sumber
Jawabannya ada di pertanyaan:
Tidak satu pun dari kedua metode ini yang mungkin, karena tipe argumen dihapus.
Namun, untuk merealisasikan metode tersebut, Anda dapat membuat enum sebagai:
sumber
public T convert(Object);
. Saya menduga metode ini misalnya dapat mempersempit sekelompok tipe yang berbeda menjadi <T>, yang misalnya adalah sebuah String. Konstruksi objek String adalah runtime - tidak ada yang dikompilasi oleh kompiler. Karenanya Anda perlu mengetahui tipe runtime, seperti String.class. Atau apakah saya melewatkan sesuatu?public final static String FOO;
dengan blok statisstatic { FOO = "bar"; }
- konstanta juga dievaluasi bahkan ketika diketahui pada waktu kompilasi.Karena kamu tidak bisa. Serius. Itu bisa ditambahkan ke spesifikasi bahasa. Belum. Ini akan menambah kompleksitas. Manfaat biaya itu berarti itu bukan prioritas tinggi.
Pembaruan: Saat ini sedang ditambahkan ke bahasa di bawah JEP 301: Enhanced Enums .
sumber
Ada metode lain di ENUM yang tidak akan berfungsi. Apa yang akan
MyEnum.values()
kembali?Bagaimana dengan
MyEnum.valueOf(String name)
?Untuk valueOf jika Anda berpikir bahwa kompiler dapat membuat metode generik seperti
untuk menyebutnya seperti
MyEnum<String> myStringEnum = MyEnum.value("some string property")
, itu tidak akan berhasil. Misalnya bagaimana jika Anda meneleponMyEnum<Int> myIntEnum = MyEnum.<Int>value("some string property")
? Tidak mungkin untuk mengimplementasikan metode itu agar berfungsi dengan benar, misalnya untuk melempar pengecualian atau mengembalikan nol ketika Anda memanggilnyaMyEnum.<Int>value("some double property")
karena jenis penghapusan.sumber
MyEnum<?>[] values()
danMyEnum<?> valueOf(...)
MyEnum<Int> blabla = valueOf("some double property");
karena jenisnya tidak kompatibel. Juga Anda ingin dalam hal itu menjadi nol karena Anda ingin mengembalikan MyEnum <Int> yang tidak ada untuk nama properti ganda dan Anda tidak dapat membuat metode itu berfungsi dengan baik karena penghapusan.Enum
memiliki banyak fitur berguna lainnya, dan yang ini akan opsional dan juga sangat berguna ...Terus terang ini sepertinya lebih dari solusi dalam mencari masalah daripada apa pun.
Seluruh tujuan java enum adalah untuk memodelkan enumerasi instance type yang berbagi properti serupa dengan cara yang memberikan konsistensi dan kekayaan di luar representasi String atau Integer yang sebanding.
Ambil contoh enum buku teks. Ini tidak terlalu berguna atau konsisten:
Mengapa saya ingin planet saya yang berbeda memiliki konversi tipe generik yang berbeda? Masalah apa yang dipecahkannya? Apakah itu membenarkan rumitnya semantik bahasa? Jika saya perlu perilaku ini merupakan alat terbaik untuk mencapainya?
Selain itu, bagaimana Anda mengelola konversi yang rumit?
misalnya
Cukup mudah dengan
String
Integer
memasukkan nama atau urutan. Tetapi obat generik memungkinkan Anda memasok jenis apa pun. Bagaimana Anda mengelola konversiMyComplexClass
? Sekarang Anda membangun dua konstruksi dengan memaksa kompiler untuk mengetahui bahwa ada subset terbatas dari tipe yang dapat dipasok ke enum generik dan memperkenalkan kebingungan tambahan pada konsep (Generics) yang sudah tampak menghindari banyak programmer.sumber
interface Converter<IN, OUT> { OUT convert(IN in); } <E> Set<E> convertListToSet(List<E> in, Converter<? super List<E>, ? extends Set<E>> converter) { return converter.convert(in); }
Kita harus bekerja secara manual setiap kali jenis yang dikonsumsi dan yang diproduksi, dan menentukan batas sesuai.Karena "enum" adalah singkatan untuk enumerasi. Itu hanya satu set konstanta bernama yang berdiri di tempat nomor urut untuk membuat kode lebih mudah dibaca.
Saya tidak melihat apa arti yang dimaksud dari konstanta tipe-parameter bisa.
sumber
java.lang.String
:public static final Comparator<String> CASE_INSENSITIVE_ORDER
. Kamu lihat sekarang :-)CASE_INSENSITIVE_ORDER
... JikaComparator<T>
itu enum, mengapa tidak memiliki literal dengan ikatan terkait<T>
?Saya pikir karena pada dasarnya Enums tidak dapat dipasang
Di mana Anda mengatur kelas T, jika JVM mengizinkan Anda untuk melakukannya?
Enumerasi adalah data yang seharusnya selalu sama, atau setidaknya, bahwa itu tidak akan berubah secara dinamis.
MyEnum baru <> ()?
Tetap saja pendekatan berikut ini mungkin bermanfaat
sumber
setO()
metode ini padaenum
. Saya menganggap konstanta enum dan bagi saya itu menyiratkan kekekalan . Jadi, bahkan jika itu mungkin, saya tidak akan melakukannya.