Java 8 akışının .min () ve .max (): bu neden derleniyor?


215

Not: Bu soru, önceki bir SO sorusu olan ölü bir bağlantıdan kaynaklanmaktadır, ancak işte gidiyor ...

Bu kodu ( not: Bu kod "işe yaramaz" ve bu Integer::comparekullanılması gerektiğini biliyorum - ben sadece bağlı sorudan ayıklayın ):

final ArrayList <Integer> list 
    = IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toList());

System.out.println(list.stream().max(Integer::max).get());
System.out.println(list.stream().min(Integer::min).get());

Bir javadoc göre .min()ve .max()her ikisinin bağımsız değişkeni bir olmalıdır Comparator. Ancak burada yöntem referansları Integersınıfın statik yöntemlerine ilişkindir.

Peki, bu neden derleniyor?


6
Düzgün çalışmadığını, ve Integer::compareyerine kullanması gerektiğini unutmayın . Integer::maxInteger::min
Christoffer Hammarström

@ ChristofferHammarström Bunu biliyorum; kod özü "Ben biliyorum, bu saçma" önce nasıl söyledi
fge

3
Seni düzeltmeye çalışmıyordum, genel olarak insanlara söylüyorum. Eğer saçma parçası yöntemleri olduğunu düşündük sanki ses yapılan Integeryöntemleri değildir Comparator.
Christoffer Hammarström

Yanıtlar:


242

Burada neler olduğunu açıklayayım, çünkü belli değil!

İlk olarak, akıştaki öğelerin minimum veya maksimum bulmak için çok fazla endişelenmenize gerek olmayan en uygun sırayla birbirleriyle karşılaştırılabileceği Stream.max()bir örneği kabul eder Comparator.

Soru şu: Tabii ki, neden Integer::maxkabul ediliyor? Sonuçta bir karşılaştırıcı değil!

Cevap, yeni lambda işlevselliğinin Java 8'de çalışma biçimindedir. Gayri resmi olarak "tek soyut yöntem" arabirimleri veya "SAM" arabirimleri olarak bilinen bir konsepte dayanır. Fikir, soyut bir yönteme sahip herhangi bir arabirimin, yöntem imzası arabirimdeki bir yöntemle eşleşen herhangi bir lambda veya yöntem başvurusu tarafından otomatik olarak uygulanabilmesidir. Yani Comparatorarayüzü incelemek (basit sürüm):

public Comparator<T> {
    T compare(T o1, T o2);
}

Bir yöntem bir yöntem arıyorsa Comparator<Integer>, o zaman bu imzayı arar:

int xxx(Integer o1, Integer o2);

"XXX" kullanıyorum çünkü yöntem adı eşleme amacıyla kullanılmıyor .

Bu nedenle, hem Integer.min(int a, int b)ve hem de Integer.max(int a, int b)otomatik boksun Comparator<Integer>bir yöntem bağlamında görünmesine izin verecek kadar yakındır .


28
ya da alternatif olarak: list.stream().mapToInt(i -> i).max().get().
assylias

13
@assylias Sen kullanmak istediğiniz .getAsInt()yerine get()bir ilgileniyor gibi olsa OptionalInt.
skiwi

... sadece denediğimiz bir max()işleve özel bir karşılaştırıcı sağlamak olduğunda !
Manu343726

Bu "SAM Arabirimi" nin aslında "İşlevsel Arayüz" olarak adlandırıldığını ve Comparatorbelgelere baktığımızda ek açıklama ile süslendiğini görebildiğimizi belirtmek gerekir @FunctionalInterface. Bu dekoratör verir büyüdür Integer::maxve Integer::minbir çevrilmek üzere Comparator.
Chris Kerekes

2
@ChrisKerekes dekoratör @FunctionalInterfacetemelde sadece dokümantasyon amaçlıdır, çünkü derleyici bunu tek bir soyut yöntemle herhangi bir arayüzle mutlu bir şekilde yapabilir.
errantlinguist

117

Comparatora, işlevsel bir arayüz ve Integer::max(dikkate alınır / kutudan çıkarma Autoboxing sonra) bu arayüz ile uyumludur. İki intdeğer alır ve bir int- beklediğiniz gibi Comparator<Integer>(yine Tamsayı / int farkını yoksaymak için gözlerini kısarak) a döndürür.

Ancak, ben doğru olanı, verilen yapmak istemem Integer.maxuymamaktadır semantik ait Comparator.compare. Ve aslında gerçekten işe yaramaz. Örneğin, küçük bir değişiklik yapın:

for (int i = 1; i <= 20; i++)
    list.add(-i);

... ve şimdi maxdeğer -20 ve mindeğer -1.

Bunun yerine, her iki çağrı da Integer::compareşunları kullanmalıdır :

System.out.println(list.stream().max(Integer::compare).get());
System.out.println(list.stream().min(Integer::compare).get());

1
Fonksiyonel arayüzleri biliyorum; ve neden yanlış sonuçlar verdiğini biliyorum. Derleyicinin bana nasıl bağırmadığını merak ediyorum
fge

6
@fge: Belirsiz olduğun kutudan çıkarma mıydı? (Ben buna pek de parçası haline bakmadım.) Bir Comparator<Integer>olurdu int compare(Integer, Integer)... Bu akla durgunluk veren değil Java yöntemi referansı izin verdiğini int max(int, int)buna dönüştürmek için ...
Jon Skeet

7
@fge: Derleyicinin anlambilimini bilmesi gerekiyor Integer::maxmu? Onun bakış açısından, şartnamesine uyan bir fonksiyona geçtiniz, gerçekten devam edebilecek olan bu.
Mark Peters

6
@fge: Özellikle, olup bitenlerin bir kısmını anlıyorsanız , ancak bunun belirli bir yönüyle ilgileniyorsanız, insanların zaten bildiğiniz bitleri açıklamak için zamanlarını boşa harcamamasını önlemek için soruda bunu açıkça belirtmek gerekir.
Jon Skeet

20
Bence altta yatan problem türünün imzasıdır Comparator.compare. Bir dönmelidir enumait {LessThan, GreaterThan, Equal}değil, bir int. Bu şekilde, işlevsel arayüz aslında eşleşmez ve bir derleme hatası alırsınız. IOW: tip imzası, Comparator.compareiki nesneyi karşılaştırmanın ne anlama geldiğinin anlamsallığını yeterince yakalamamaktadır ve bu nedenle, yanlışlıkla nesneyi karşılaştırmakla hiçbir ilgisi olmayan diğer arabirimler aynı tip imzasına sahiptir.
Jörg W Mittag

19

Bu Integer::min, Comparator<Integer>arabirimin uygulanmasını çözdüğü için çalışır .

Yöntem, referans Integer::mingiderir için Integer.min(int a, int b), giderilmiştir IntBinaryOperatorve muhtemelen Autoboxing yere bir hale oluşur BinaryOperator<Integer>.

Ve min()solunum max()yöntemleri Stream<Integer>sormak Comparator<Integer>uygulanacak arayüz.
Şimdi bu tek yönteme çözümleniyor Integer compareTo(Integer o1, Integer o2). Hangi tür BinaryOperator<Integer>.

Ve böylece sihir her iki yöntem de bir olduğu için oldu BinaryOperator<Integer>.


Integer::minUyguladığını söylemek tamamen doğru değil Comparable. Herhangi bir şeyi uygulayabilecek bir tür değildir. Ancak uygulayan bir nesne olarak değerlendirilir Comparable.
Lii

1
@Lii Teşekkürler, şimdi düzelttim.
skiwi

Comparator<Integer>tek soyut yöntem ("işlevsel" olarak da bilinir) arayüzüdür ve Integer::minsözleşmesini yerine getirir, böylece lambda bu şekilde yorumlanabilir. BinaryOperator burada (veya IntBinaryOperator, ya da) geliyor görmek nasıl bilmiyorum - bu ve Karşılaştırıcı arasında hiçbir alt tip ilişkisi yoktur.
Paŭlo Ebermann

2

David M. Lloyd tarafından verilen bilgilerin yanı sıra, buna izin veren mekanizmaya hedef yazma denir .

Fikir, derleyicinin lambda ifadelerine veya yöntem referanslarına atadığı türün yalnızca ifadenin kendisine değil, aynı zamanda kullanıldığı yere de bağlı olmasıdır.

Bir ifadenin hedefi, sonucunun atandığı değişken veya sonucunun geçirildiği parametredir.

Lambda ifadelerine ve yöntem referanslarına, böyle bir tür bulunabiliyorsa, hedeflerinin türüyle eşleşen bir tür atanır.

Daha fazla bilgi için Java Eğiticisindeki Tür Çıkarma bölümüne bakın .


1

Benim çözüm oldu böylece max ve min alma bir dizi ile bir hata vardı:

int max = Arrays.stream(arrayWithInts).max().getAsInt();
int min = Arrays.stream(arrayWithInts).min().getAsInt();
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.