Pertanyaan Mengapa di java enum dinyatakan sebagai Enum > [duplikat]


Kemungkinan Duplikat:
definisi java Enum 

Pertanyaan yang dirumuskan lebih baik, yang tidak dianggap sebagai duplikat:
Apa yang akan berbeda di Jawa jika deklarasi Enum tidak memiliki bagian rekursif

jika desainer bahasa hanya menggunakan Enum <E memperluas Enum> bagaimana itu akan mempengaruhi bahasa?

Satu-satunya perbedaan sekarang adalah seseorang yang bisa menulis

A memperluas Enum <B>

tetapi karena tidak diperbolehkan di Jawa untuk memperpanjang enums yang akan tetap ilegal. Saya juga berpikir tentang seseorang yang memasok jvm bytecode yang mendefinisikan smth sebagai memperluas enum - tetapi generik tidak dapat mempengaruhi itu karena mereka semua terhapus.

Jadi apa inti dari deklarasi tersebut?

Terima kasih!

Edit untuk kesederhanaan, mari kita lihat sebuah contoh:

antarmuka MyComparable <T> {
    int myCompare (T o);
}

class MyEnum <E extends MyEnum> mengimplementasikan MyComparable <E> {
    public int myCompare (E o) {return -1; }
}

kelas FirstEnum memperluas MyEnum <FirstEnum> {}

kelas SecondEnum memperluas MyEnum <SecondEnum> {}

ada apa dengan struktur kelas ini? Apa yang bisa dilakukan yang "MyEnum <E extends MyEnum <E >>" akan membatasi?


32
2018-06-17 12:55


asal


Jawaban:


Ini adalah pertanyaan umum, dan bisa dimengerti. Silahkan lihat ini bagian dari FAQ umum untuk jawabannya (dan sebetulnya, bacalah sebanyak-banyaknya dari seluruh dokumen karena Anda merasa nyaman dengannya, ini lebih baik dilakukan dan informatif).

Jawaban singkatnya adalah bahwa hal itu memaksa kelas untuk menjadi parameter pada dirinya sendiri; ini diperlukan untuk superclasses untuk menentukan metode, menggunakan parameter generik, yang bekerja secara transparan ("asli", jika Anda mau) dengan subclass mereka.

Edit: Sebagai contoh (non) misalnya, pertimbangkan clone() metode aktif Object. Saat ini, itu didefinisikan untuk mengembalikan nilai jenis Object. Berkat jenis kovarian kembali, subkelas khusus dapat (dan sering kali) menentukan bahwa mereka mengembalikan kelas yang lebih spesifik, tetapi ini tidak dapat ditegakkan dan karenanya tidak dapat disimpulkan untuk kelas arbitrer.

Sekarang, jika Objek didefinisikan seperti Enum, yaitu Object<T extends Object<T>> maka Anda harus mendefinisikan semua kelas sebagai sesuatu seperti public class MyFoo<MyFoo>. Karena itu, clone() dapat dinyatakan mengembalikan jenis T dan Anda dapat memastikan pada waktu kompilasi bahwa nilai yang dikembalikan selalu persis kelas yang sama dengan objek itu sendiri (bahkan subclass tidak akan cocok dengan parameter).

Sekarang dalam kasus ini, Object tidak diparameterkan seperti ini karena akan sangat menjengkelkan untuk memiliki bagasi ini di semua kelas ketika 99% dari mereka tidak akan menggunakannya sama sekali. Tapi untuk beberapa hirarki kelas itu bisa sangat berguna - saya telah menggunakan teknik yang sama sebelumnya dengan tipe abstrak, pengurai ekspresi rekursif dengan beberapa implementasi. Konstruksi ini memungkinkan penulisan kode yang "jelas" tanpa harus mentransmisikan di mana-mana, atau menyalin-dan-tempel hanya untuk mengubah definisi kelas konkret.

Edit 2 (Untuk benar-benar menjawab pertanyaan Anda!):

Jika Enum didefinisikan sebagai Enum<E extends Enum>, maka seperti yang Anda katakan, seseorang dapat mendefinisikan kelas sebagai A extends Enum<B>. Ini mengalahkan titik dari konstruk generik, yang memastikan bahwa parameter generik selalu tepat jenis kelas yang dimaksud. Memberikan contoh konkret, Enum menyatakan metode compareTo sebagai

public final int compareTo(E o)

Dalam hal ini, sejak Anda ditentukan A untuk memperpanjang Enum<B>, contoh dari A hanya bisa dibandingkan dengan contoh B (apapun B), yang hampir pasti tidak terlalu berguna. Dengan konstruk tambahan, Anda tahu itu apa saja kelas yang memperpanjang Enum hanya sebanding dengan dirinya sendiri. Dan karenanya Anda dapat memberikan implementasi metode di superclass yang tetap berguna, dan spesifik, di semua subclass.

(Tanpa trik generik rekursif ini, satu-satunya pilihan lain adalah mendefinisikan compareTo as public final int compareTo(Enum o). Ini tidak benar-benar hal yang sama, seperti yang bisa membandingkan satu java.math.RoundingMode melawan java.lang.Thread.State tanpa kompilator mengeluh, yang sekali lagi tidak terlalu berguna.)


OK, mari kita menjauh Enum sendiri seperti yang kita tampaknya terpaku padanya. Sebaliknya, di sini adalah kelas abstrak:

public abstract class Manipulator<T extends Manipulator<T>>
{
    /**
     * This method actually does the work, whatever that is
     */
    public abstract void manipulate(DomainObject o);

    /**
     * This creates a child that can be used for divide and conquer-y stuff
     */
    public T createChild()
    {
        // Some really useful implementation here based on
        // state contained in this class
    }
}

Kami akan memiliki beberapa implementasi konkret dari ini - SaveToDatabaseManipulator, SpellCheckingManipulator, apa pun. Selain itu kami juga ingin membiarkan orang mendefinisikannya sendiri, karena ini adalah kelas yang sangat berguna. ;-)

Sekarang - Anda akan melihat bahwa kami menggunakan definisi generik rekursif, dan kemudian kembali T dari createChild metode. Ini berarti bahwa:

1) Kami tahu dan kompilator tahu bahwa jika saya memanggil:

SpellCheckingManipulator obj = ...; // We have a reference somehow
return obj.createChild();

maka nilai yang dikembalikan sudah pasti a SpellCheckingManipulator, meskipun menggunakan definisi dari superclass. Generik rekursif di sini memungkinkan compiler untuk mengetahui apa yang jelas bagi kita, jadi Anda tidak perlu terus memberikan nilai kembali (seperti yang sering Anda lakukan dengan clone(), sebagai contoh).

2) Perhatikan bahwa saya tidak menyatakan akhir metode, karena mungkin beberapa subclass khusus akan ingin mengesampingkannya dengan versi yang lebih cocok untuk mereka sendiri. Definisi generik berarti bahwa terlepas dari siapa yang membuat kelas baru atau bagaimana definisi itu ditentukan, kita masih dapat menegaskan bahwa kembalinya dari misalnya BrandNewSloppilyCodedManipulator.createChild() akan tetap menjadi turunan dari BrandNewSloppilyCodedManipulator. Jika pengembang yang ceroboh mencoba untuk mendefinisikannya kembali saja Manipulator, kompilator tidak akan membiarkannya. Dan jika mereka mencoba mendefinisikan kelas sebagai BrandNewSloppilyCodedManipulator<SpellCheckingManipulator>, itu tidak akan membiarkan mereka.

Pada dasarnya, kesimpulannya adalah bahwa trik ini berguna ketika Anda ingin menyediakan beberapa fungsi dalam superclass yang entah bagaimana mendapat lebih spesifik dalam subclass. Dengan menyatakan superclass seperti ini, Anda mengunci ke bawah parameter umum untuk setiap subclass menjadi subkelas itu sendiri. Inilah sebabnya mengapa Anda bisa menulis generik compareTo atau createChild metode di superclass dan mencegahnya menjadi terlalu samar ketika Anda sedang berhadapan dengan subclass tertentu.


33
2018-06-17 12:57



Satu-satunya perbedaan sekarang adalah seseorang yang bisa menulis

A extends Enum<B>

tetapi karena tidak diperbolehkan di Jawa untuk memperpanjang enums yang akan tetap ilegal. Saya juga berpikir tentang seseorang yang memasok jvm bytecode yang mendefinisikan smth sebagai memperluas enum - tetapi generik tidak dapat mempengaruhi itu karena mereka semua terhapus.

Anda benar bahwa bahasa itu akan mencegah seseorang melakukan hal itu. Namun, manfaat membuatnya E extends Enum<E> bukan hanya extends Enum adalah bahwa beberapa metode di Enum sekarang akan mengembalikan jenis yang tepat dan spesifik.

Poster itu meminta sesuatu yang tidak akan berhasil. Jika Enum didefinisikan sebagai Enum<E>, maka Anda bisa melakukan ini:

// Would be legal, because element types of `DaysOfWeek` and `Color` both
//   extend `Enum<E>`, not `Enum<E extends Enum<E>>`.
DaysOfWeek d = Enum.valueOf(Color.class, "Purple");

0
2018-06-17 13:02