Java 8 스트림의 .min () 및 .max () : 왜 컴파일됩니까?


215

참고 :이 질문은 이전 SO 질문 인 데드 링크에서 비롯되었지만 여기에 있습니다 ...

이 코드를 참조하십시오 ( 참고 :이 코드는 "작동하지 않는다"는 것을 알고 Integer::compare있어야합니다. 링크 된 질문에서 추출했습니다 .

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());

.min()및 의 javadoc에 따르면 .max()둘 다의 인수는이어야합니다 Comparator. 그러나 여기에서 메소드 참조는 Integer클래스의 정적 메소드에 대한 것 입니다.

그렇다면 왜 이것이 전혀 컴파일되지 않습니까?


6
제대로 작동하지 않으므로 및 Integer::compare대신 사용해야합니다 . Integer::maxInteger::min
Christoffer Hammarström

@ ChristofferHammarström 나는 그것을 알고있다. 코드 추출 전에 "어떻게
말하면

3
나는 당신을 고치려고하지 않았습니다. 나는 사람들에게 일반적으로 말하고 있습니다. 터무니없는 부분은의 방법이의 방법 Integer이 아니라고 생각하는 것처럼 들리게됩니다 Comparator.
Christoffer Hammarström

답변:


242

명확하지 않기 때문에 여기서 일어나는 일을 설명하겠습니다!

먼저 스트림의 항목을 서로 비교하여 너무 걱정할 필요가없는 최적의 순서로 최소 또는 최대를 찾을 수 있도록 Stream.max()인스턴스를 수락합니다 Comparator.

물론 문제는 왜 Integer::max받아 들여 집니까? 결국 그것은 비교기가 아닙니다!

새로운 람다 기능이 Java 8에서 작동하는 방식이 정답입니다. 이는 "단일 추상 메소드"인터페이스 또는 "SAM"인터페이스로 알려진 개념에 의존합니다. 아이디어는 하나의 추상 메소드가있는 인터페이스가 메소드 서명이 인터페이스의 한 메소드와 일치하는 람다 (또는 메소드 참조)에 의해 자동으로 구현 될 수 있다는 것입니다. 따라서 Comparator인터페이스 (단순 버전)를 검사하십시오 .

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

메소드가를 찾고 있다면 Comparator<Integer>본질적으로이 서명을 찾고있는 것입니다.

int xxx(Integer o1, Integer o2);

메소드 이름이 일치 목적으로 사용되지 않기 때문에 "xxx"를 사용합니다 .

따라서, 모두 Integer.min(int a, int b)Integer.max(int a, int b)오토 박싱이은으로 표시 할 수 있음을 충분히 가까이있는 Comparator<Integer>방법의 맥락에서가.


28
또는 대안 적으로 : list.stream().mapToInt(i -> i).max().get().
assylias 2014 년

13
@assylias를 처리 할 때 .getAsInt()대신에 사용하려고 get()합니다 OptionalInt.
skiwi

... 우리가 시도하는 것은 max()함수에 맞춤 비교기를 제공하는 것입니다!
Manu343726

이 "SAM 인터페이스"는 실제로 "기능 인터페이스"라고하며 Comparator설명서를 보면 주석으로 장식되어 있음을 알 수 있습니다 @FunctionalInterface. 이 장식은 수있는 마법 Integer::maxInteger::min로 변환 할 Comparator.
Chris Kerekes

2
@ChrisKerekes 데코레이터 @FunctionalInterface는 주로 하나의 추상 메소드로 모든 인터페이스 에서이 작업을 행복하게 수행 할 수 있기 때문에 기본적으로 문서 목적으로 만 사용됩니다.
잘못된 표현

117

Comparator기능적인 인터페이스 이며 Integer::max해당 인터페이스를 준수합니다 (오토 박스 / 언 박스를 고려한 후). 두 개의 int값을 가져 와서 inta Comparator<Integer>를 기대하는 것처럼-를 반환합니다 (다시 말하면 정수 / 정수 차이를 무시하기 위해 가늘게 뜨고 있습니다).

그러나 Integer.max의미 를 준수하지 않는 한 올바른 일을 기대 하지는 않습니다 Comparator.compare. 실제로 실제로는 일반적으로 작동하지 않습니다. 예를 들어, 한 가지 작은 변경을 수행하십시오.

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

... 이제 max값은 -20이고 min값은 -1입니다.

대신 두 통화 모두 다음을 사용해야합니다 Integer::compare.

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

1
기능적 인터페이스에 대해 알고 있습니다. 왜 그것이 잘못된 결과를 제공하는지 알고 있습니다. 난 그냥 지구상에서 어떻게 컴파일러가 소리 지르지 않았는지 궁금해
fge

6
@fge : 글쎄요, 당신이 잘 모르는 개봉입니까? (정확히 그 부분을 살펴 보지 않았습니다.) A Comparator<Integer>int compare(Integer, Integer)... Java가 메소드 참조 int max(int, int)를 변환하는 것을 허용한다는 것은 당황스럽지 않습니다 ...
Jon Skeet

7
@ fge : 컴파일러는의 의미를 알고 있어야 Integer::max합니까? 그 관점에서 당신은 그 사양을 만족시키는 함수를 전달했습니다.
Mark Peters

6
@ fge : 특히, 진행중인 일의 일부 를 이해 하지만 그 특정 측면에 대해 흥미가 있다면 사람들이 이미 알고있는 비트를 설명하는 데 시간을 낭비하지 않도록 질문에서 분명히하는 것이 좋습니다.
Jon Skeet

20
근본적인 문제는의 형식 서명이라고 생각합니다 Comparator.compare. 그것은 반환해야 enum{LessThan, GreaterThan, Equal}아닌를 int. 그렇게하면 기능적 인터페이스가 실제로 일치하지 않으며 컴파일 오류가 발생합니다. IOW :의 유형 서명은 Comparator.compare두 객체를 비교한다는 의미의 의미를 적절히 캡처하지 못하므로 객체 비교와 전혀 관련이없는 다른 인터페이스는 실수로 동일한 유형 서명을 갖습니다.
Jörg W Mittag

19

이것은 인터페이스 Integer::min의 구현으로 해석 되기 때문에 작동 합니다 Comparator<Integer>.

방법 참조 Integer::min결의를하기 Integer.min(int a, int b)에 해결 IntBinaryOperator, 그리고 아마도 오토 박싱 어딘가에 그것을 발생합니다 BinaryOperator<Integer>.

그리고 min()resp max()메소드 Stream<Integer>Comparator<Integer>인터페이스를 구현 하도록 요청합니다 .
이제 이것은 단일 방법으로 해결됩니다 Integer compareTo(Integer o1, Integer o2). 어떤 유형 BinaryOperator<Integer>입니다.

따라서 마술은 두 방법이 모두 그렇듯이 일어났습니다 BinaryOperator<Integer>.


Integer::min구현 한다고 말하는 것은 완전히 정확하지 않습니다 Comparable. 어떤 것도 구현할 수있는 유형이 아닙니다. 그러나 구현하는 객체로 평가됩니다 Comparable.
Lii

1
@Lii 감사합니다. 지금 바로 고쳤습니다.
skiwi

Comparator<Integer>단일 추상 방법 (일명 "기능적") 인터페이스이며 Integer::min계약을 이행하므로 람다는 이것을 다음과 같이 해석 할 수 있습니다. BinaryOperator가 여기에서 어떻게 작동하는지 알지 못합니다 (또는 IntBinaryOperator). 그와 Comparator 사이에는 하위 유형 관계가 없습니다.
Paŭlo Ebermann

2

David M. Lloyd가 제공 한 정보 외에도이를 허용하는 메커니즘을 대상 입력 이라고 합니다.

컴파일러가 람다 식 또는 메서드 참조에 할당하는 형식은 식 자체뿐만 아니라 사용되는 위치에 따라 달라집니다.

표현식의 대상은 결과가 지정된 변수 또는 결과가 전달되는 매개 변수입니다.

Lambda 표현식 및 메소드 참조에는 해당 유형을 찾을 수있는 경우 대상의 유형과 일치하는 유형이 지정됩니다.

자세한 정보는 Java 학습서 의 유형 유추 섹션 을 참조하십시오.


1

내 솔루션이 최대와 최소값을 얻는 배열에 오류가 발생했습니다.

int max = Arrays.stream(arrayWithInts).max().getAsInt();
int min = Arrays.stream(arrayWithInts).min().getAsInt();
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.