Generik di kelas Java Comparator. Apa persisnya tipe T dan U?
Pertimbangkan dua metode berikut. Satu-satunya perbedaan mereka adalah dalam tipe deklarasi Fungsi <> yang umum
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
public static <T, U extends Comparable<? super U>> Comparator<T> comparingT(
Function<T, ? extends U> keyExtractor) <-- Check here! T instead of ? super T
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
Katakanlah saya punya List<GamingComputer> = { ASUS, MSI }, di mana GamingComputermeluas Computer. Sekarang, saya ingin menyortirnya.
List.sort( comparing( Computer::getProperty ) )
Apa tipe T?
Intuisi saya: T=GamingComputer. comparing()mengambil keyExtractor, tipe siapa Function<Computer super GamingComputer, Property>. Akhirnya, comparing()kembali Comparator<GamingComputer>.
Kode ini, membuktikan intuisi saya, terkompilasi dengan sempurna:
Function<Computer, Property> f1 = Computer::getProperty;
Comparator<GamingComputer> c1 = comparing(f1);
Sekarang, dengan PECS, karena c1, c2ditambahkan ke collection / konstruktor / metode, selama collection menangani kelas induknya, ia bisa menangani kelas anak apa pun. Itulah alasan di baliknya <? super T>.
Seperti yang ditunjukkan dalam kode ini:
Function<Computer, Property> f2 = Computer::getProperty;
Comparator<GamingComputer> c2 = comparingT(f2); // FAILS to compile. Required Type: Comparator<GamingComputer>, Provided Comparator<Computer>
Comparator<Computer> c2 = comparingT(f2); // compiles successfuly
Karena f2bekerja dengan semua Computer, itu harus bisa bekerja dengan semua GamingComputerjuga. Namun, karena kami tidak mendeklarasikan tipe sebagai <? super T>, kami tidak dapat membuat Comparatordari GamingComputers.
Masuk akal. Kemudian...
Comparator<GamingComputer> c22 = comparingT(Computer::getProperty); // compiles successfuly... WT, excuse mi French, heck???
Dugaan saya: comparingT()dengan tipe T=GamingComputermemaksa tertunduk keyExtractor, yaitu Computer::getProperty. Itu memaksa semua Computersuntuk menggunakan GamingComputer::getProperty, yang mungkin bukan masalah, karena Comparator<GamingComputer>memang membandingkan GamingComputers.
Tapi, mengapa ini TIDAK dikompilasi?
Function<Computer, Property> f22 = GamingComputer::getProperty;
Kesalahannya sangat aneh:
Metode non-statis tidak dapat dirujuk dari konteks statis, yang mungkin merupakan bug dari Intellij
Metode non-statis tidak dapat dirujuk dari konteks statis di aliran java 8
Namun, saat menyusun:
java: incompatible types: invalid method reference
method getPart in class function.GamingComputer cannot be applied to given types
required: no arguments
found: function.Computer
reason: actual and formal argument lists differ in length
Jawaban
Intuisi saya:
T=GamingComputer
Intuisi Anda benar.
Mengapa
Comparator<GamingComputer> c22 = comparingT(Computer::getProperty);mengkompilasi?
Ini karena tidak seperti contoh antarmuka fungsional yang tidak berubah, referensi metode bersifat kovarian dan kontravarian. Menggunakan contoh dari sini , Anda dapat melakukan sesuatu seperti:
// in SomeClass
public static Integer function(Object o) {
return 2;
}
// ...
Function<String, Object> function = SomeCLass::function;
Atau menggunakan kelas Anda, Anda dapat melakukan:
Function<GamingComputer, Property> f = Computer::getProperty;
Ini "seolah-olah" referensi metode ada ? superpada parameter mereka dan ? extendspada tipe kembalian! Detail tentang apa yang berhasil dan apa yang tidak ditentukan di bagian 15.13.2 dari Spesifikasi Bahasa Java.
Jadi untuk c22, Tmasih GamingComputer. Referensi metode Computer::getPropertydapat diubah Function<GamingComputer, Property>menjadi referensi metode.
Ini tidak dapat dikompilasi, meskipun f2"menyimpan" Computer::getProperty:
Comparator<GamingComputer> c2 = comparingT(f2);
karena f2bukan referensi metode itu sendiri. Ini adalah variabel.
Mengapa
Function<Computer, Property> f22 = GamingComputer::getProperty;tidak dikompilasi?
f22akan dapat menerima segala jenis Computer, karena ia menerima Computer. Jika Anda memberikan f22komputer jenis lain (bukan GamingComputer), GamingComputer.getPropertytentu tidak akan bisa mengatasinya, bukan?