Saya pikir saya memahami generik Java dengan cukup baik, tetapi kemudian saya menemukan yang berikut di java.lang.Enum:
class Enum<E extends Enum<E>>
Bisakah seseorang menjelaskan bagaimana menafsirkan parameter tipe ini? Poin bonus untuk memberikan contoh lain di mana parameter tipe yang sama dapat digunakan.
Jawaban:
Ini berarti bahwa argumen tipe untuk enum harus berasal dari enum yang itu sendiri memiliki argumen tipe yang sama. Bagaimana ini bisa terjadi? Dengan membuat argumen type maka tipe baru itu sendiri. Jadi jika saya punya enum yang disebut StatusCode, itu akan setara dengan:
Sekarang jika Anda memeriksa kendala, kami punya
Enum<StatusCode>
- jadiE=StatusCode
. Mari kita periksa: apakahE
memperpanjangEnum<StatusCode>
? Iya! Kami baik-baik sajaAnda mungkin bertanya pada diri sendiri apa gunanya ini :) Yah, itu berarti bahwa API untuk Enum dapat merujuk pada dirinya sendiri - misalnya, bisa mengatakan
Enum<E>
implementasinyaComparable<E>
. Kelas dasar dapat melakukan perbandingan (dalam hal enum) tetapi dapat memastikan bahwa itu hanya membandingkan jenis enum yang tepat satu sama lain. (EDIT: Ya, hampir - lihat hasil edit di bagian bawah.)Saya telah menggunakan sesuatu yang serupa di porta C # dari ProtocolBuffers. Ada "pesan" (tidak berubah) dan "pembangun" (bisa berubah, digunakan untuk membangun pesan) - dan mereka datang sebagai pasangan jenis. Antarmuka yang terlibat adalah:
Ini berarti bahwa dari pesan Anda bisa mendapatkan pembangun yang sesuai (misalnya untuk mengambil salinan pesan dan mengubah beberapa bit) dan dari pembangun Anda bisa mendapatkan pesan yang sesuai ketika Anda selesai membangunnya. Ini adalah pekerjaan yang baik, pengguna API tidak perlu benar-benar peduli tentang ini - ini sangat rumit, dan butuh beberapa iterasi untuk sampai ke tempatnya.
EDIT: Perhatikan bahwa ini tidak menghentikan Anda dari membuat tipe aneh yang menggunakan argumen tipe yang itu sendiri tidak apa-apa, tetapi yang bukan tipe yang sama. Tujuannya untuk memberi manfaat dengan benar kasus yang daripada melindungi Anda dari kasus yang salah .
Jadi jika
Enum
tidak ditangani "khusus" di Java, Anda dapat (seperti tercantum dalam komentar) membuat jenis berikut:Second
akan menerapkanComparable<First>
daripadaComparable<Second>
... tapiFirst
itu sendiri akan baik-baik saja.sumber
Enum
tidak memiliki metode instan yang mengembalikan tipe parameter tipe.class Enum<E>
sudah cukup dalam semua kasus. Dan di Generics Anda hanya boleh menggunakan ikatan yang lebih ketat jika memang benar-benar diperlukan untuk memastikan keamanan jenis.Enum
subclass tidak selalu software otomatis, satu-satunya alasan Anda akan perluclass Enum<E extends Enum<?>>
lebihclass Enum<E>
adalah kemampuan untuk aksesordinal
untukcompareTo()
. Namun, jika Anda memikirkannya, tidak masuk akal dari sudut pandang bahasa untuk memungkinkan Anda membandingkan dua jenis enum melalui tata cara mereka. Oleh karena itu, implementasiEnum.compareTo()
yang menggunakanordinal
hanya masuk akal dalam konteksEnum
subclass yang di-autogenerasi. Jika Anda dapat secara manual subkelasEnum
,compareTo
mungkin harusabstract
.Berikut ini adalah versi penjelasan yang dimodifikasi dari buku Java Generics and Collections : Kami telah
Enum
mendeklarasikannyayang akan diperluas ke kelas
di mana
...
menjadi kelas dasar entah bagaimana parameter untuk Enums. Mari kita cari tahu apa yang harus terjadi. Nah, salah satu syaratnyaSeason
adalah harus diterapkanComparable<Season>
. Jadi kita perluApa yang bisa Anda gunakan untuk
...
memungkinkan ini berfungsi? Mengingat bahwa itu harus menjadi parameterisasiEnum
, satu-satunya pilihan adalahEnum<Season>
, sehingga Anda dapat memiliki:Jadi
Enum
parameterisasi pada jenis sukaSeason
. Abstrak dariSeason
dan Anda mendapatkan bahwa parameterEnum
adalah jenis apa pun yang memuaskanMaurice Naftalin (penulis bersama, Java Generics and Collections)
sumber
Season
menerapkan ituComparable<Season>
?compareTo
metode harus dideklarasikan sebagaiEnum
subtipe, atau kompiler akan (dengan benar) mengatakan bahwa itu tidak memiliki ordinal.Enum
, maka akan mungkin untuk memilikinyaclass OneEnum extends Enum<AnotherEnum>{}
, bahkan dengan caraEnum
yang dinyatakan sekarang. Ini tidak akan membuat banyak akal untuk dapat membandingkan satu jenis enum dengan yang lain, sehingga kemudianEnum
'scompareTo
tidak masuk akal sebagaimana dinyatakan pula. Batas tidak memberikan bantuan apa pun untuk ini.public class Enum<E extends Enum<?>>
itu juga sudah cukup.Ini dapat diilustrasikan dengan contoh sederhana dan teknik yang dapat digunakan untuk mengimplementasikan panggilan metode berantai untuk sub-kelas. Dalam contoh di bawah ini,
setName
pengembalianNode
rantai tidak akan berfungsi untukCity
:Jadi kita bisa mereferensikan sub-kelas dalam deklarasi generik, sehingga
City
sekarang mengembalikan tipe yang benar:sumber
return (CHILD) this;
Pertimbangkan untuk menambahkan metode getThis ():protected CHILD getThis() { return this; }
Lihat: angelikalanger.com/GenericsFAQ/FAQSections/…Node<T>
bukan itu masalahnya), saya mengabaikannya untuk menghemat waktu.return (SELF) this;
dikompilasi ke dalamreturn this;
, jadi Anda bisa tinggalkan saja.Anda bukan satu-satunya yang bertanya-tanya apa artinya itu; lihat blog Java Chaotic .
"Jika kelas memperluas kelas ini, itu harus melewati parameter E. Batas E parameter adalah untuk kelas yang memperluas kelas ini dengan parameter E yang sama".
sumber
Posting ini benar-benar menjelaskan kepada saya masalah 'tipe generik rekursif' ini. Saya hanya ingin menambahkan kasus lain di mana struktur khusus ini diperlukan.
Misalkan Anda memiliki node generik dalam grafik generik:
Maka Anda dapat memiliki grafik jenis khusus:
sumber
class Foo extends Node<City>
tempat Foo tidak terkait dengan City.class Node<T>
?class Node<T>
sepenuhnya konsisten dengan contoh Anda.Jika Anda melihat
Enum
kode sumber, ia memiliki yang berikut ini:Hal pertama yang pertama, apa
E extends Enum<E>
artinya? Itu berarti parameter tipe adalah sesuatu yang memanjang dari Enum, dan tidak diparametisasi dengan tipe mentah (itu diparametisasi dengan sendirinya).Ini relevan jika Anda memiliki enum
yang, jika saya tahu benar, diterjemahkan ke
Jadi ini berarti bahwa MyEnum menerima metode berikut:
Dan yang lebih penting,
Ini membuat
getDeclaringClass()
gips keClass<T>
objek yang tepat .Contoh cara yang lebih jelas adalah yang saya jawab pada pertanyaan ini di mana Anda tidak dapat menghindari konstruk ini jika Anda ingin menentukan batasan umum.
sumber
compareTo
ataugetDeclaringClass
membutuhkanextends Enum<E>
batasan.Menurut wikipedia, pola ini disebut pola templat berulang yang menarik . Pada dasarnya, dengan menggunakan pola CRTP, kita dapat dengan mudah merujuk ke tipe subclass tanpa tipe casting, yang berarti dengan menggunakan pola, kita dapat meniru fungsi virtual.
sumber