Java 8 Comparator 유형 추론에 의해 매우 혼동 됨
특히 정적 메서드 사용과 람다 식에 매개 변수 형식이 필요한지 여부와 관련하여 Collections.sort
및 의 차이점을 살펴 보았습니다 . 시작하기 전에, 예 를 들어 내 문제를 극복하기 위해 메서드 참조를 사용할 수 있다는 것을 알고 있지만 여기에서 내 쿼리는 수정하고 싶은 것이 아니라 대답을 원하는 것입니다. 즉, Java 컴파일러가이 방식으로 처리하는 이유 .list.sort
Comparator
Song::getTitle
이것이 내 발견입니다. 일부 노래가 추가 된 ArrayList
유형 이 있다고 가정 Song
하면 세 가지 표준 get 메소드가 있습니다.
ArrayList<Song> playlist1 = new ArrayList<Song>();
//add some new Song objects
playlist.addSong( new Song("Only Girl (In The World)", 235, "Rhianna") );
playlist.addSong( new Song("Thinking of Me", 206, "Olly Murs") );
playlist.addSong( new Song("Raise Your Glass", 202,"P!nk") );
다음은 작동하는 두 가지 유형의 정렬 방법에 대한 호출입니다.
Collections.sort(playlist1,
Comparator.comparing(p1 -> p1.getTitle()));
playlist1.sort(
Comparator.comparing(p1 -> p1.getTitle()));
체인을 시작하자마자 thenComparing
다음이 발생합니다.
Collections.sort(playlist1,
Comparator.comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
playlist1.sort(
Comparator.comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
즉, p1
더 이상 유형을 알지 못하기 때문에 구문 오류 입니다. 이 문제를 해결하기 위해 Song
비교의 첫 번째 매개 변수에 유형 을 추가합니다 .
Collections.sort(playlist1,
Comparator.comparing((Song p1) -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
playlist1.sort(
Comparator.comparing((Song p1) -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
이제 혼란스러운 부분이 있습니다. p laylist1.sort
, 즉 List의 경우 다음 thenComparing
호출 모두에 대해 모든 컴파일 오류를 해결 합니다. 그러나의 Collections.sort
경우 첫 번째 문제는 해결하지만 마지막 문제는 해결하지 않습니다. 나는 몇 가지 추가 호출을 테스트 했으며 매개 변수를 thenComparing
입력하지 않는 한 항상 마지막 호출에 대한 오류를 표시합니다 (Song p1)
.
이제를 만들고 TreeSet
사용하여 이것을 추가로 테스트했습니다 Objects.compare
.
int x = Objects.compare(t1, t2,
Comparator.comparing((Song p1) -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
Set<Song> set = new TreeSet<Song>(
Comparator.comparing((Song p1) -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
TreeSet
에서는 컴파일 오류가 없지만 Objects.compare
마지막 호출에서 thenComparing
오류 를 표시하는 것과 동일한 일이 발생 합니다.
누구든지 이것이 왜 발생하는지 그리고 (Song p1)
단순히 비교 메서드를 호출 할 때 (추가 thenComparing
호출 없이 ) 사용할 필요가없는 이유를 설명해 주시겠습니까?
동일한 주제에 대한 또 다른 쿼리는 다음과 같이 할 때입니다 TreeSet
.
Set<Song> set = new TreeSet<Song>(
Comparator.comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
즉 Song
, 비교 메서드 호출에 대한 첫 번째 람다 매개 변수에서 유형 을 제거하면 비교 호출과 첫 번째 호출에서 구문 오류가 표시 thenComparing
되지만 최종 호출에는 표시되지 않습니다 thenComparing
. 위에서 발생한 것과 거의 반대입니다! 다른 모든 3 예로, 즉 위해, 반면에 Objects.compare
, List.sort
그리고 Collections.sort
그 첫번째 제거 할 때 Song
모든 통화에 대해 PARAM 유형이 쇼 구문 오류를.
미리 감사드립니다.
Eclipse Kepler SR2에서 수신 한 오류의 스크린 샷을 포함하도록 편집되었습니다. 이제 이클립스에만 해당됩니다. 명령 줄에서 JDK8 자바 컴파일러를 사용하여 컴파일 할 때 정상적으로 컴파일되기 때문입니다.
첫째, 당신이 말하는 모든 예제는 참조 구현 (JDK 8의 javac)을 사용하여 오류가 잘 컴파일됩니다. 또한 IntelliJ에서도 잘 작동하므로보고있는 오류는 Eclipse에만 해당됩니다.
귀하의 근본적인 질문은 "연결을 시작할 때 작동이 중지되는 이유"입니다. 그 이유는 람다 식과 제네릭 메서드 호출이 메서드 매개 변수로 나타날 때는 폴리 식 (그 유형은 상황에 따라 다름)이지만 메서드 수신자 식으로 대신 나타날 때는 그렇지 않기 때문입니다.
당신이 말할 때
Collections.sort(playlist1, comparing(p1 -> p1.getTitle()));
there is enough type information to solve for both the type argument of comparing()
and the argument type p1
. The comparing()
call gets its target type from the signature of Collections.sort
, so it is known comparing()
must return a Comparator<Song>
, and therefore p1
must be Song
.
But when you start chaining:
Collections.sort(playlist1,
comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist()));
now we've got a problem. We know that the compound expression comparing(...).thenComparing(...)
has a target type of Comparator<Song>
, but because the receiver expression for the chain, comparing(p -> p.getTitle())
, is a generic method call, and we can't infer its type parameters from its other arguments, we're kind of out of luck. Since we don't know the type of this expression, we don't know that it has a thenComparing
method, etc.
There are several ways to fix this, all of which involve injecting more type information so that the initial object in the chain can be properly typed. Here they are, in rough order of decreasing desirability and increasing intrusiveness:
- Use an exact method reference (one with no overloads), like
Song::getTitle
. This then gives enough type information to infer the type variables for thecomparing()
call, and therefore give it a type, and therefore continue down the chain. - Use an explicit lambda (as you did in your example).
- Provide a type witness for the
comparing()
call:Comparator.<Song, String>comparing(...)
. - Provide an explicit target type with a cast, by casting the receiver expression to
Comparator<Song>
.
The problem is type inferencing. Without adding a (Song s)
to the first comparison, comparator.comparing
doesn't know the type of the input so it defaults to Object.
You can fix this problem 1 of 3 ways:
Use the new Java 8 method reference syntax
Collections.sort(playlist, Comparator.comparing(Song::getTitle) .thenComparing(Song::getDuration) .thenComparing(Song::getArtist) );
Pull out each comparison step into a local reference
Comparator<Song> byName = (s1, s2) -> s1.getArtist().compareTo(s2.getArtist()); Comparator<Song> byDuration = (s1, s2) -> Integer.compare(s1.getDuration(), s2.getDuration()); Collections.sort(playlist, byName .thenComparing(byDuration) );
EDIT
Forcing the type returned by the Comparator (note you need both the input type and the comparison key type)
sort( Comparator.<Song, String>comparing((s) -> s.getTitle()) .thenComparing(p1 -> p1.getDuration()) .thenComparing(p1 -> p1.getArtist()) );
I think the "last" thenComparing
syntax error is misleading you. It's actually a type problem with the whole chain, it's just the compiler only marking the end of the chain as a syntax error because that's when the final return type doesn't match I guess.
I'm not sure why List
is doing a better inferencing job than Collection
since it should do the same capture type but apparently not.
playlist1.sort(...)
creates a bound of Song for the type variable E, from the declaration of playlist1, which "ripples" to the comparator.
In Collections.sort(...)
, there is no such bound, and the inference from the type of the first comparator is not enough for the compiler to infer the rest.
I think you would get "correct" behavior from Collections.<Song>sort(...)
, but don't have a java 8 install to test it out for you.
Another way to deal with this compile time error:
Cast your first comparing function's variable explicitly and then good to go. I have sort the list of org.bson.Documents object. Please look at sample code
Comparator<Document> comparator = Comparator.comparing((Document hist) -> (String) hist.get("orderLineStatus"), reverseOrder())
.thenComparing(hist -> (Date) hist.get("promisedShipDate"))
.thenComparing(hist -> (Date) hist.get("lastShipDate"));
list = list.stream().sorted(comparator).collect(Collectors.toList());
참고URL : https://stackoverflow.com/questions/24436871/very-confused-by-java-8-comparator-type-inference
'code' 카테고리의 다른 글
JSLint : 정의 된 오류 전에 함수 사용 (0) | 2020.10.28 |
---|---|
nVidia Quadro와 Geforce 카드의 차이점은 무엇입니까? (0) | 2020.10.27 |
Visual Studio 2015 충돌 (0) | 2020.10.27 |
크기 조정을 위해 h1 ~ h6을 사용하여 어레이에서 태그 클라우드를 생성하는 가장 좋은 방법은 무엇입니까? (0) | 2020.10.27 |
Emacs의 패키지 관리자에게 무엇을 기대합니까? (0) | 2020.10.27 |