Ekspresi Lambda dan metode umum

111

Misalkan saya memiliki antarmuka umum:

interface MyComparable<T extends Comparable<T>>  {
    public int compare(T obj1, T obj2);
}

Dan metode sort:

public static <T extends Comparable<T>> 
       void sort(List<T> list, MyComparable<T> comp) {
    // sort the list
}

Saya bisa memanggil metode ini dan meneruskan ekspresi lambda sebagai argumen:

List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));

Itu akan bekerja dengan baik.

Tetapi sekarang jika saya membuat antarmuka non-generik, dan metode generik:

interface MyComparable {
    public <T extends Comparable<T>> int compare(T obj1, T obj2);
}

public static <T extends Comparable<T>> 
       void sort(List<T> list, MyComparable comp) {
}

Dan kemudian panggil ini seperti:

List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));

Itu tidak dapat dikompilasi. Ini menunjukkan kesalahan pada ekspresi lambda yang mengatakan:

"Metode target bersifat umum"

Oke, ketika saya kompilasi menggunakan javac, itu menunjukkan kesalahan berikut:

SO.java:20: error: incompatible types: cannot infer type-variable(s) T#1
        sort(list, (a, b) -> a.compareTo(b));
            ^
    (argument mismatch; invalid functional descriptor for lambda expression
      method <T#2>(T#2,T#2)int in interface MyComparable is generic)
  where T#1,T#2 are type-variables:
    T#1 extends Comparable<T#1> declared in method <T#1>sort(List<T#1>,MyComparable)
    T#2 extends Comparable<T#2> declared in method <T#2>compare(T#2,T#2)
1 error

Dari pesan error ini, sepertinya compiler tidak dapat menyimpulkan argumen type. Apakah itu masalahnya? Jika ya, mengapa bisa terjadi seperti ini?

Saya mencoba berbagai cara, mencari melalui internet. Kemudian saya menemukan artikel JavaCodeGeeks ini , yang menunjukkan caranya, jadi saya mencoba:

sort(list, <T extends Comparable<T>>(a, b) -> a.compareTo(b));

yang lagi-lagi tidak berfungsi, bertentangan dengan klaim artikel itu bahwa itu berfungsi. Mungkin itu dulu berfungsi di beberapa build awal.

Jadi pertanyaan saya adalah: Apakah ada cara untuk membuat ekspresi lambda untuk metode umum? Saya dapat melakukan ini menggunakan referensi metode, dengan membuat metode:

public static <T extends Comparable<T>> int compare(T obj1, T obj2) {
    return obj1.compareTo(obj2);
}

di beberapa kelas katakan SO, dan berikan sebagai:

sort(list, SO::compare);
Rohit Jain
sumber

Jawaban:

117

Anda tidak dapat menggunakan ekspresi lambda untuk antarmuka fungsional , jika metode dalam antarmuka fungsional memiliki parameter tipe . Lihat bagian §15.27.3 di JLS8 :

Ekspresi lambda kompatibel [..] dengan tipe target T jika T adalah tipe antarmuka fungsional (§9.8) dan ekspresi kongruen dengan tipe fungsi [..] T. [..] Ekspresi lambda kongruen dengan jenis fungsi jika semua hal berikut ini benar:

  • Jenis fungsi tidak memiliki parameter jenis .
  • [..]
nosid
sumber
47
Namun, batasan ini tidak berlaku untuk referensi metode ke metode umum. Anda dapat menggunakan referensi metode ke metode generik dengan antarmuka fungsional generik.
Brian Goetz
17
Saya yakin ada alasan bagus untuk pembatasan ini. Apa itu?
Sandro
6
@ Sandro: tidak ada sintaks untuk mendeklarasikan parameter tipe untuk ekspresi lambda. Dan sintaksis seperti itu akan sangat rumit. Perlu diingat bahwa parser masih harus dapat membedakan ekspresi lambda dengan parameter tipe selain dari konstruksi Java legal lainnya. Jadi, Anda harus menggunakan referensi metode. Metode target dapat mendeklarasikan parameter tipe menggunakan sintaks yang telah ditetapkan.
Holger
2
@Holger masih, di mana parameter tipe dapat dideduksi secara otomatis, kompilator dapat mendeklarasikan tipe tangkap seperti ketika Anda mendeklarasikan misalnya Set <?> Dan melakukan pemeriksaan tipe dengan tipe yang ditangkap tersebut. Tentu, itu tidak memungkinkan untuk memberikannya sebagai parameter tipe dalam tubuh, tetapi jika Anda membutuhkannya, menggunakan referensi metode adalah alternatif yang baik
WorldSEnder
17

Menggunakan referensi metode, saya menemukan cara lain untuk meneruskan argumen:

List<String> list = Arrays.asList("a", "b", "c");        
sort(list, Comparable::<String>compareTo);
Andrey
sumber
3

Cukup tunjuk kompilator versi yang tepat dari Pembanding generik dengan (Comparator<String>)

Jadi jawabannya adalah

sort(list, (Comparator<String>)(a, b) -> a.compareTo(b));

Aleч
sumber
2
incompatible types: java.util.Comparator<java.lang.String> cannot be converted to MyComparabledan MyComparabletidak generik (tanpa tipe) jadi (MyComparable<String>)tidak akan berhasil
user85421
1
Tidak tahu bagaimana Anda mengetik kode @CarlosHeuberger, tetapi berfungsi dengan sangat baik untuk saya, inilah yang saya cari.
Ivan Perales M.
@Rumahsakitotak. setelah 2 bulan ... Saya menyalin & menempel kode Anda dan menyalin & menempelkan baris di atas pada baris sortir Anda - hanya itu, seperti di sini: ideone.com/YNwBbF ! Apakah Anda yakin Anda benar-benar mengetik kode di atas? menggunakan Compartor?
pengguna85421
Tidak, saya tidak, saya menggunakan ide di balik jawaban, untuk memberikan fungsi untuk memberi tahu kompilasi jenis apa itu dan itu berhasil.
Ivan Perales M.
@Rumahsakitotak. Nah, lalu apa masalah Anda dengan "mengetik" saya? Jawabannya, seperti yang diposting, tidak berfungsi.
pengguna85421
0

Apakah maksudmu seperti ini?:

<T,S>(T t, S s)->...

Jenis apa lambda ini? Anda tidak dapat mengungkapkannya di Java dan oleh karena itu tidak dapat membuat ekspresi ini dalam aplikasi fungsi dan ekspresi harus dapat disusun.

Agar kebutuhan ini berhasil, Anda memerlukan dukungan untuk Jenis Peringkat2 di Java.

Metode diperbolehkan untuk menjadi umum tetapi oleh karena itu Anda tidak dapat menggunakannya sebagai ekspresi. Namun, mereka dapat dikurangi menjadi ekspresi lambda dengan mengkhususkan semua tipe generik yang diperlukan sebelum Anda dapat meneruskannya:ClassName::<TypeName>methodName

iconfly
sumber
1
"Of what type is this lambda? You couldn't express that in Java..."Jenisnya akan disimpulkan menggunakan konteks, sama seperti lambda lainnya. Jenis lambda tidak diekspresikan secara eksplisit di lambda itu sendiri.
Kröw