특히 정적 메서드 사용과 람다 식에 매개 변수 형식이 필요한지 여부와 관련하여 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()));
의 유형 인수 comparing()
및 인수 유형 둘 다에 대해 해결할 충분한 유형 정보가 p1
있습니다. comparing()
호출의 서명에서 목표 형식을 가져옵니다 Collections.sort
, 그래서 알려져있다 comparing()
을 반환해야합니다 Comparator<Song>
, 따라서 p1
해야합니다 Song
.
그러나 연결을 시작할 때 :
Collections.sort(playlist1,
comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist()));
이제 우리는 문제가 있습니다. 복합 표현식 comparing(...).thenComparing(...)
의 대상 유형 이라는 것을 알고 Comparator<Song>
있지만 체인에 대한 수신자 표현식 comparing(p -> p.getTitle())
은 일반 메소드 호출이고 다른 인수에서 유형 매개 변수를 유추 할 수 없기 때문에 운이 좋지 않습니다. . 이 식의 유형을 모르기 때문에 thenComparing
메서드 등 이 있는지 알 수 없습니다 .
이 문제를 해결하는 방법에는 여러 가지가 있으며, 모두 더 많은 유형 정보를 주입하여 체인의 초기 객체를 올바르게 입력 할 수 있습니다. 다음은 욕구 감소와 침입 증가의 대략적인 순서입니다.
- 와 같은 정확한 메서드 참조 (오버로드가없는 참조)를 사용합니다
Song::getTitle
. 그러면comparing()
호출에 대한 유형 변수를 추론 할 수있는 충분한 유형 정보가 제공되므로 유형을 지정하고 체인 아래로 계속 진행합니다. - 예제에서했던 것처럼 명시 적 람다를 사용합니다.
comparing()
호출에 대한 유형 증인을 제공하십시오Comparator.<Song, String>comparing(...)
..- 수신자 표현식을로 캐스트하여 캐스트가있는 명시 적 대상 유형을 제공하십시오
Comparator<Song>
.
답변
문제는 유형 추론입니다. (Song s)
첫 번째 비교에를 추가 comparator.comparing
하지 않으면 입력 유형을 알지 못하므로 기본값은 Object입니다.
다음 세 가지 방법 중 하나로이 문제를 해결할 수 있습니다.
-
새로운 Java 8 메서드 참조 구문 사용
Collections.sort(playlist, Comparator.comparing(Song::getTitle) .thenComparing(Song::getDuration) .thenComparing(Song::getArtist) );
-
각 비교 단계를 로컬 참조로 가져옵니다.
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) );
편집하다
-
Comparator에서 반환 된 유형 강제 적용 (입력 유형과 비교 키 유형이 모두 필요함)
sort( Comparator.<Song, String>comparing((s) -> s.getTitle()) .thenComparing(p1 -> p1.getDuration()) .thenComparing(p1 -> p1.getArtist()) );
“마지막” thenComparing
구문 오류가 오해의 소지가 있다고 생각합니다 . 그것은 실제로 전체 체인의 유형 문제입니다. 최종 반환 유형이 일치하지 않는 경우이기 때문에 체인의 끝을 구문 오류로 표시하는 컴파일러 일뿐입니다.
동일한 캡처 유형을 수행해야하지만 분명히 그렇지 않기 때문에 왜 List
더 나은 추론 작업을 수행 하는지 잘 모르겠습니다 Collection
.
답변
이 컴파일 시간 오류를 처리하는 또 다른 방법 :
첫 번째 비교 함수의 변수를 명시 적으로 캐스팅 한 다음 사용하면됩니다. org.bson.Documents 개체 목록을 정렬했습니다. 샘플 코드를보세요
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());
답변
playlist1.sort(...)
재생 목록 1의 선언에서 비교기로 “잔물결”하는 형식 변수 E에 대한 노래 경계를 만듭니다.
에는 Collections.sort(...)
그러한 경계가 없으며 첫 번째 비교기 유형의 추론만으로는 컴파일러가 나머지를 추론하기에 충분하지 않습니다.
나는 당신이에서 “올바른”동작을 얻을 것이라고 생각하지만 당신을 위해 Collections.<Song>sort(...)
그것을 테스트하기 위해 자바 8이 설치되어 있지 않다.