Uwaga: to pytanie pochodzi z martwego linku, który był poprzednim pytaniem SO, ale tutaj idzie ...
Zobacz ten kod ( uwaga: wiem, że ten kod nie będzie „działał” i że Integer::compare
należy go użyć - właśnie wyodrębniłem go z połączonego pytania ):
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());
Zgodnie z javadoc .min()
i .max()
argumentem obu powinno być Comparator
. Jednak tutaj odniesienia do metod dotyczą metod statycznych Integer
klasy.
Dlaczego w ogóle się to kompiluje?
java
java-8
java-stream
fge
źródło
źródło
Integer::compare
zamiastInteger::max
iInteger::min
.Integer
nie są metodamiComparator
.Odpowiedzi:
Pozwól mi wyjaśnić, co się tutaj dzieje, ponieważ nie jest to oczywiste!
Po pierwsze,
Stream.max()
akceptuje takie wystąpienieComparator
, aby elementy w strumieniu mogły być porównywane względem siebie w celu znalezienia minimum lub maksimum, w optymalnej kolejności, o którą nie musisz się zbytnio przejmować.Pytanie zatem, dlaczego jest
Integer::max
akceptowane? W końcu to nie jest komparator!Odpowiedź jest taka, że nowa funkcjonalność lambda działa w Javie 8. Opiera się ona na koncepcji, która jest nieformalnie znana jako interfejsy „pojedynczej metody abstrakcyjnej” lub interfejsy „SAM”. Chodzi o to, że każdy interfejs z jedną metodą abstrakcyjną może być automatycznie implementowany przez dowolną lambda - lub odwołanie do metody - której sygnatura metody jest zgodna z jedną metodą w interfejsie. Zatem badanie
Comparator
interfejsu (wersja prosta):Jeśli metoda szuka a
Comparator<Integer>
, to zasadniczo szuka tej sygnatury:Używam „xxx”, ponieważ nazwa metody nie jest używana w celu dopasowania .
Dlatego zarówno
Integer.min(int a, int b)
iInteger.max(int a, int b)
są na tyle blisko, że autoboxing pozwoli to pojawiał się jakoComparator<Integer>
w kontekście metody.źródło
list.stream().mapToInt(i -> i).max().get()
..getAsInt()
zamiastget()
chociaż, że masz do czynienia ze związkiemOptionalInt
.max()
funkcji!Comparator
dokumentację możemy zauważyć, że jest ozdobiony adnotacją@FunctionalInterface
. Ten dekorator to magia, która pozwalaInteger::max
iInteger::min
przekształca się wComparator
.@FunctionalInterface
służy głównie do celów dokumentacyjnych, ponieważ kompilator może to zrobić z dowolnym interfejsem za pomocą jednej abstrakcyjnej metody.Comparator
jest interfejsem funkcjonalnym iInteger::max
jest zgodny z tym interfejsem (po uwzględnieniu autoboxowania / rozpakowywania). Przyjmuje dwieint
wartości i zwraca anint
- dokładnie tak, jak można się tego spodziewaćComparator<Integer>
(ponownie, mrużąc oczy, aby zignorować różnicę całkowitą / całkowitą).Jednak nie spodziewałbym się to zrobić dobry uczynek, zważywszy, że
Integer.max
nie są zgodne z semantyką oComparator.compare
. I tak naprawdę to w ogóle nie działa. Na przykład wprowadź jedną małą zmianę:... a teraz
max
wartość wynosi -20, amin
wartość wynosi -1.Zamiast tego oba połączenia powinny używać
Integer::compare
:źródło
Comparator<Integer>
Miałbymint compare(Integer, Integer)
... to nie zadziwiające, że Java pozwalaint max(int, int)
na konwersję referencji metod do tego ...Integer::max
? Z jego perspektywy przekazałeś funkcję spełniającą jej specyfikację, to wszystko, co naprawdę może trwać.Comparator.compare
. Należy zwracaćenum
o{LessThan, GreaterThan, Equal}
, a nieint
. W ten sposób funkcjonalny interfejs nie byłby właściwie zgodny i pojawiałby się błąd kompilacji. IOW: podpis typuComparator.compare
nie odpowiednio przechwytuje semantykę tego, co oznacza porównywanie dwóch obiektów, a zatem inne interfejsy, które absolutnie nie mają nic wspólnego z porównywaniem obiektów, przypadkowo mają ten sam podpis.Działa to, ponieważ
Integer::min
rozwiązuje się do implementacjiComparator<Integer>
interfejsu.Odniesienie do metody resolves
Integer::min
toInteger.min(int a, int b)
, resolved toIntBinaryOperator
i prawdopodobnie autoboxing występuje gdzieś, co czyni ją aBinaryOperator<Integer>
.I
min()
odpowiedniomax()
metodyStream<Integer>
zapytajComparator<Integer>
interfejsu, który ma zostać zaimplementowany.Teraz rozwiązuje to jedną metodę
Integer compareTo(Integer o1, Integer o2)
. Który jest typuBinaryOperator<Integer>
.I tak magia się wydarzyła, ponieważ obie metody są
BinaryOperator<Integer>
.źródło
Integer::min
implementujeComparable
. To nie jest typ, który może zaimplementować wszystko. Ale jest przetwarzany w obiekt, który implementujeComparable
.Comparator<Integer>
jest interfejsem z pojedynczą abstrakcyjną metodą („funkcjonalną”) iInteger::min
spełnia swój kontrakt, więc lambda może być interpretowana w ten sposób. Nie wiem, jak widzisz BinaryOperator wchodzący tutaj (lub IntBinaryOperator) - nie ma związku między tym a Komparatoriem.Oprócz informacji podanych przez Davida M. Lloyda można dodać, że mechanizm, który na to pozwala, nazywa się typowaniem celu .
Chodzi o to, że typ, który kompilator przypisuje do wyrażeń lambda lub odwołań do metody, nie zależy tylko od samego wyrażenia, ale także od miejsca jego użycia.
Celem wyrażenia jest zmienna, do której przypisany jest jego wynik lub parametr, do którego przekazywany jest wynik.
Wyrażeniom lambda i referencjom do metod przypisywany jest typ, który odpowiada typowi ich celu, jeśli taki typ można znaleźć.
Aby uzyskać więcej informacji, zobacz sekcję Wnioskowanie typu w samouczku Java.
źródło
Miałem błąd z uzyskaniem przez tablicę maksimum i min, więc moim rozwiązaniem było:
źródło