3 minute read ◦ posted on 05 Jul 2022 by Hanno Embregts .

In the summer of 2021, I got my Java 11 certification. I expected it to be quite a breeze, because I’d been a Java developer for 14 years and surely I should have seen it all by now, right? Turned out I was very wrong. I came across lots of things that I didn’t even know were possible with Java. In this weekly blog series I will go through 11 of these ‘crazy learnings’ that surprised me the most, even as an experienced developer. This week we dive into stream elements.

Stream elements should implement Comparable

Imagine we are building a conference schedule, and to that end we want to produce a list of talks that is sorted by the start times.

Well, when the Talk class looks like this…

public class Talk {
    private final String speaker;
    private final String title;
    private final LocalTime startTime;

    public Talk(String speaker, String title, LocalTime startTime) {
        this.speaker = speaker;
        this.title = title;
        this.startTime = startTime;
    }
}

…we can obtain the sorted talk list by using a stream of Talks and sorting it.

return Stream.of(
        new Talk("Bugs Bunny", "Carrots Are Awesome!", LocalTime.of(11, 0)),
        new Talk("Road Runner", "Stop Living Too Slow", LocalTime.of(9, 30)),
        new Talk("Tweety", "Ban All Cats Off The Internet", LocalTime.of(14, 45))
).sorted().collect(Collectors.toCollection(TreeSet::new));

That’s the theory, at least. Because when I ran this piece of code for the first time, this is what I got:

java.lang.ClassCastException: class com.github.hannotify.elevencrazyjavathings.number10.Talk cannot be cast to class java.lang.Comparable (com.github.hannotify.elevencrazyjavathings.number10.Talk is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
    at java.base/java.util.Comparators$NaturalOrderComparator.compare(Comparators.java:47)
    at java.base/java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
    at java.base/java.util.TimSort.sort(TimSort.java:220)
    at java.base/java.util.Arrays.sort(Arrays.java:1515)
    (...)

It certainly looks like the sorting is not working yet. And the error message is quite unusual, as it doesn’t seem to have anything to do with sorting in the first place. The key here is the mention of the Comparable interface. Apparently the sorted() method that we’re calling on the stream expects its elements to implement Comparable.

And sure enough, when we get to the JavaDoc of the method Stream.sorted(), this is what we read:

Returns a stream consisting of the elements of this stream, sorted according to natural order. If the elements of this stream are not Comparable, a java.lang.ClassCastException may be thrown when the terminal operation is executed.

Well, now it makes perfect sense! So to get this example working, we should make sure that Talk implements Comparable:

class Talk implements Comparable<Talk> {
    private final String speaker;
    private final String title;
    private final LocalTime startTime;

    public Talk(String speaker, String title, LocalTime startTime) {
        this.speaker = speaker;
        this.title = title;
        this.startTime = startTime;
    }

    @Override
    public int compareTo(Talk otherTalk) {
        return startTime.compareTo(otherTalk.startTime) ;
    }
}

Alternatively, we can call the overloaded sorted() method, which takes a Comparator:

return Stream.of(
        new Talk("Bugs Bunny", "Carrots Are Awesome!", LocalTime.of(11, 0)),
        new Talk("Road Runner", "Stop Living Too Slow", LocalTime.of(9, 30)),
        new Talk("Tweety", "Ban All Cats Off The Internet", LocalTime.of(14, 45))
).sorted(Comparator.comparing(Talk::getStartTime)).collect(Collectors.toCollection(TreeSet::new));

But for this to work you would need to add a getStartTime() method to the Talk class, of course.

So this is how you can sort stream elements, even when you’re struggling with a few ClassCastExpections. Next week we’ll take a look at static interface methods!

Clock

Image from PxHere

Other blog posts in this series

Did you miss a blog post in this series? Here’s a list of all posts that have been published so far:

  1. A few freaky array declarations
  2. Stream elements should implement Comparable
  3. Accessing static interface methods
  4. Anonymous subclasses in enums
  5. Division by zero
  6. Method overloading priorities
  7. The crazy stuff that is allowed in switch statements
  8. Equality in cloned arrays
  9. Wrapper objects: some are more equal than others
  10. Functional interfaces actually CAN contain multiple abstract methods
  11. Passing arguments to method references