Lambda İfadesi ve genel yöntem


111

Genel bir arayüzüm olduğunu varsayalım:

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

Ve bir yöntem sort:

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

Bu yöntemi çağırabilir ve argüman olarak bir lambda ifadesi geçirebilirim:

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

Bu iyi çalışacak.

Ama şimdi arayüzü genel olmayan ve yöntemi genel yaparsam:

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) {
}

Ve sonra bunu şöyle çağırın:

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

Derlemez. Lambda ifadesinde şunu söyleyen hatayı gösterir:

"Hedef yöntem geneldir"

Tamam, kullanarak derlediğimde javacaşağıdaki hatayı gösteriyor:

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

Bu hata mesajından, derleyicinin tür bağımsız değişkenlerini çıkaramadığı görülmektedir. Durum bu mu? Cevabınız evet ise, neden böyle oluyor?

İnternette araştırdım, çeşitli yollar denedim. Sonra bir yol gösteren bu JavaCodeGeeks makalesini buldum , bu yüzden denedim:

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

bu makalenin işe yaradığını iddia ettiğinin aksine yine çalışmıyor. Bazı ilk yapılarda çalışıyor olması mümkün olabilir.

Öyleyse sorum şu: Genel bir yöntem için lambda ifadesi oluşturmanın herhangi bir yolu var mı? Bunu bir yöntem referansı kullanarak bir yöntem oluşturarak yapabilirim:

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

bazı sınıflarda şunu söyleyin SOve geçin:

sort(list, SO::compare);

Yanıtlar:


117

Bir kullanamazsınız lambda ifade bir için işlevsel bir arayüz yöntemi ise, işlevsel bir arayüz vardır tip parametrelerini . Bkz JLS8 bölümünde §15.27.3 :

Bir lambda ifadesi, bir hedef türü ile [..] uyumlu T ise , T fonksiyonel arabirimi (§9.8) ve ifade uyumlu fonksiyonu tipi [..] T. [..] Lambda ifade uyumlu Aşağıdakilerin tümü doğruysa bir işlev türü ile:

  • İşlev türünün tür parametresi yoktur .
  • [..]

47
Ancak bu kısıtlama, genel yöntemlere yapılan yöntem başvuruları için geçerli değildir . Genel bir işlevsel arabirime sahip genel bir yönteme bir yöntem başvurusu kullanabilirsiniz.
Brian Goetz

17
Eminim bu kısıtlamanın iyi bir nedeni vardır. Bu ne?
Sandro

6
@Sandro: Bir lambda ifadesi için tür parametrelerini bildirmek için hiçbir sözdizimi yoktur. Ve böyle bir sözdizimi çok karmaşık olurdu. Ayrıştırıcının, diğer yasal Java yapılarından farklı tür parametreleriyle böyle bir lambda ifadesini yine de söyleyebilmesi gerektiğini unutmayın. Bu nedenle yöntem referanslarına başvurmanız gerekir. Hedef yöntem , yerleşik bir sözdizimi kullanarak tür parametrelerini bildirebilir .
Holger

2
@Holger yine de, tür parametrelerinin otomatik olarak çıkarılabildiği yerlerde, derleyici bir yakalama türünü örneğin Set <?> Bildirdiğinizde ve bu yakalanan türlerle tür kontrolleri yaptığınızda olduğu gibi karartabilir. Elbette, bu onları vücutta tip parametreleri olarak vermeyi imkansız kılar, ancak buna ihtiyacınız varsa, yöntem referanslarına başvurmak iyi bir alternatiftir
WorldSEnder

17

Yöntem referansını kullanarak, argümanı iletmenin başka bir yolunu buldum:

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

3

Derleyiciye jenerik Karşılaştırıcının uygun sürümünü işaretleyin. (Comparator<String>)

Yani cevap olacak

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


2
incompatible types: java.util.Comparator<java.lang.String> cannot be converted to MyComparableve MyComparablegenel değildir (tür yok), bu nedenle (MyComparable<String>)de çalışmaz
user85421

1
@CarlosHeuberger kodunu nasıl yazdığınızı bilmiyorum, ama benim için çok iyi çalışıyor, aradığım şey buydu.
Ivan Perales M.

@IvanPeralesM. 2 ay sonra ... Kodunuzu kopyalayıp yapıştırdım ve sıralama satırınızın üstüne kopyalayıp yapıştırdım - tam da burada olduğu gibi: ideone.com/YNwBbF ! Yukarıdaki kodu tam olarak yazdığınızdan emin misiniz? kullanarak Compartor?
user85421

Hayır, bilmiyorum, cevabın arkasındaki fikri kullanıyorum, derlemeye ne tür olduğunu ve işe yaradığını söylemek için işlevselliği kullanıyorum.
Ivan Perales M.

@IvanPeralesM. peki, o zaman benim "yazarak" sorunun ne? Yanıt, yayınlandığı şekliyle çalışmıyor.
user85421

0

Bunun gibi bir şey mi ifade etmeye çalışıyorsun?:

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

Bu lambda ne tür? Bunu Java'da ifade edemezsiniz ve bu nedenle bu ifadeyi bir işlev uygulamasında oluşturamazsınız ve ifadeler oluşturulabilir olmalıdır.

Bunun işe yaraması için Java'da Rank2 Türleri desteğine ihtiyacınız olacaktır .

Yöntemlerin genel olmasına izin verilir, ancak bu nedenle bunları ifade olarak kullanamazsınız. Bununla birlikte, bunları geçirmeden önce gerekli tüm genel türleri özelleştirerek lambda ifadesine indirgenebilirler:ClassName::<TypeName>methodName


1
"Of what type is this lambda? You couldn't express that in Java..."Tür, diğer lambda'da olduğu gibi bağlam kullanılarak çıkarılır. Bir lambda türü, lambda'nın kendisinde açıkça ifade edilmez.
Kröw
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.