Java Generics Wildcarding Dengan Berbagai Kelas

385

Saya ingin memiliki objek Class, tapi saya ingin memaksa kelas apa pun yang diwakilinya untuk memperluas kelas A dan mengimplementasikan antarmuka B.

Dapat saya lakukan:

Class<? extends ClassA>

Atau:

Class<? extends InterfaceB>

tapi aku tidak bisa melakukan keduanya. Apakah ada cara untuk melakukan ini?

Alex Beardsley
sumber

Jawaban:

613

Sebenarnya, Anda dapat melakukan apa yang Anda inginkan. Jika Anda ingin memberikan banyak antarmuka atau antarmuka kelas plus, Anda harus membuat wildcard Anda terlihat seperti ini:

<T extends ClassA & InterfaceB>

Lihat Tutorial Generik di sun.com, khususnya bagian Parameter Tipe Terikat , di bagian bawah halaman. Anda sebenarnya dapat membuat daftar lebih dari satu antarmuka jika Anda mau, menggunakan & InterfaceNameuntuk masing-masing yang Anda butuhkan.

Ini bisa menjadi rumit semena-mena. Untuk mendemonstrasikan, lihat deklarasi JavaDoc Collections#max, yang (dibungkus menjadi dua baris) adalah:

public static <T extends Object & Comparable<? super T>> T
                                           max(Collection<? extends T> coll)

mengapa begitu rumit? Seperti yang dikatakan dalam FAQ Java Generics: Untuk menjaga kompatibilitas biner .

Sepertinya ini tidak berfungsi untuk deklarasi variabel, tetapi itu berfungsi ketika meletakkan batas umum pada kelas. Jadi, untuk melakukan apa yang Anda inginkan, Anda mungkin harus melewati beberapa rintangan. Tapi kamu bisa melakukannya. Anda dapat melakukan sesuatu seperti ini, meletakkan batas umum pada kelas Anda dan kemudian:

class classB { }
interface interfaceC { }

public class MyClass<T extends classB & interfaceC> {
    Class<T> variable;
}

untuk mendapatkan variableyang memiliki batasan yang Anda inginkan. Untuk informasi dan contoh lebih lanjut, lihat halaman 3 Generics in Java 5.0 . Catatan, dalam <T extends B & C>, nama kelas harus didahulukan, dan antarmuka mengikuti. Dan tentu saja Anda hanya dapat mendaftar satu kelas.

Eddie
sumber
94
Ini sangat membantu. Perlu disebutkan bahwa kelas harus didahulukan, Anda tidak bisa mengatakan '<T extends InterfaceB & ClassA>'.
EricS
5
Bagaimana Anda melakukan hal yang sama untuk T harus memperluas kelas ATAU mengimplementasikan antarmuka?
Ragunath Jawahar
1
@RagunathJawahar: Anda tidak bisa terlalu terbuka tentang hal ini. Anda harus memiliki beberapa batasan, dan salah satunya adalah mengetahui terlebih dahulu di mana Anda akan memiliki antarmuka dan di mana Anda akan memiliki kelas, dan bagaimana Anda akan menggunakan warisan. Anda cukup banyak harus tahu untuk parameter tipe apakah itu akan menjadi kelas atau antarmuka.
Eddie
4
Baris pertama jawaban Anda mengatakan "biarkan wildcard Anda terlihat seperti ini." Tidak ada ?dalam ekspresi itu, jadi apakah itu benar-benar "wildcard"? Saya bertanya karena saya tidak bisa mendapatkan keseluruhan konsep "memperluas dua hal sekaligus" agar berfungsi ketika parameter type benar-benar wildcard.
The111
1
Ya, itu sebenarnya bukan wildcard dan Anda tidak bisa melakukan apa yang diminta OP dengan wildcard. Anda dapat melakukan apa yang diinginkan OP, secara efektif, hanya saja tidak dengan wildcard.
Eddie
17

Anda tidak dapat melakukannya dengan parameter tipe "anonim" (yaitu, wildcard yang digunakan ?), tetapi Anda bisa melakukannya dengan parameter tipe "bernama". Cukup nyatakan parameter tipe pada level metode atau kelas.

import java.util.List;
interface A{}
interface B{}
public class Test<E extends B & A, T extends List<E>> {
    T t;
}
pengguna2861738
sumber
2
Mengapa wildcard tidak diizinkan? yaitu saya tidak mengerti mengapa ini tidak valid: ArrayList <? memperluas daftar ClassA & InterfaceB>; Dan kemudian jika Anda mengeluarkan elemen dari daftar, Anda bisa menetapkannya ke variabel tipe ClassA atau tipe InterfaceB
Tandai
Mengganti wildcard dengan variabel adalah teknik hebat! Tapi itu tidak selalu berhasil. Misalnya, Java tidak mengizinkan variabel tipe nama dalam tipe nilai penjelasan, sementara itu memungkinkan wildcard.
sigpwned