I've been looking at the difference between Collections.sort and list.sort, specifically regarding using the Comparator static methods and whether param types are required in the lambda expressions. Before we start, I know I could use method references, e.g. Song::getTitle to overcome my problems, but my query here is not so much something I want to fix but something I want an answer to, i.e. why is the Java compiler handling it in this way.
These are my finding. Suppose we have an ArrayList of type Song, with some songs added, there are 3 standard get methods:
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") );
Here is a call to both types of sort method that works, no problem:
Collections.sort(playlist1,
Comparator.comparing(p1 -> p1.getTitle()));
playlist1.sort(
Comparator.comparing(p1 -> p1.getTitle()));
As soon as I start to chain thenComparing, the following happens:
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())
);
i.e. syntax errors because it does not know the type of p1 anymore. So to fix this I add the type Song to the first parameter (of comparing):
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())
);
Now here comes the CONFUSING part. For playlist1.sort, i.e. the List, this solve all compilation errors, for both the following thenComparing calls. However, for Collections.sort, it solves it for the first one, but not the last one. I tested added several extra calls to thenComparing and it always shows an error for the last one, unless I put (Song p1) for the parameter.
Now I went on to test this further with creating a TreeSet and with using 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())
);
The same thing happens as in, for the TreeSet, there are no compilation errors but for Objects.compare the last call to thenComparing shows an error.
Can anyone please explain why this is happening and also why there is no need to use (Song p1) at all when simply calling the comparing method (without further thenComparing calls).
One other query on the same topic is when I do this to the TreeSet:
Set<Song> set = new TreeSet<Song>(
Comparator.comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
i.e. remove the type Song from the first lambda parameter for the comparing method call, it shows syntax errors under the call to comparing and the first call to thenComparing but not to the final call to thenComparing - almost the opposite of what was happening above! Whereas, for all the other 3 examples i.e. with Objects.compare, List.sort and Collections.sort when I remove that first Song param type it shows syntax errors for all the calls.
Edited to include screenshot of errors I was receiving in Eclipse Kepler SR2, which I have now since found are Eclipse specific because when compiled using the JDK8 java compiler on the command-line it compiles OK.
