Why does Temporal not extend Comparable in Java 8 jsr310

Attempts were made to implement Comparable, but because Java does not have self-type generics, it was necessary to have Temporal generified by its subtype (like Enum). In practice, this was not a good trade off, as in 95%+ usages of Temporal, the generified parameter would be unknown and thus Temporal<?>. Since the only generified solution was verbose and impractical to most users, it was not retained.

As JB Nizet's answer says, you can just cast to Comparable in most cases. Providing the two inputs to compareTo are of the same concrete type, you should see no problems.

On intervals, my suspicion is that a LocalDateRange, an InstantInterval and a LocalTimeInterval have less in common than might be imagined and a generified solution is probably worse than coding three separate classes. Remember that is OK to choose against using generics providing the the trade-offs have been considered.


The answer of @JBNizet was very obscure for me on first glance because he advises to do a simple type-cast to Comparable (ignoring compiler warning) and generally I would prefer code without any type-casts or warnings (they are not out there just for fun), but first now I find the time to investigate the whole thing more carefully. Let's consider following simple interval example:

public class FlexInterval<T extends Temporal & Comparable<T>> {

    private final T from;
    private final T to;

    public FlexInterval(T from, T to) {
        super();
        this.from = from;
        this.to = to;
    }

    public boolean contains(T test) {
        return (this.from.compareTo(test) <= 0) && (this.to.compareTo(test) >= 0);
    }
}

On that base (preferred by OP as far as I have understood him) it is logical that the compiler will reject first line in following code:

FlexInterval<LocalDate> interval = 
  new FlexInterval<LocalDate>(today, today); // compile-error
System.out.println(interval.contains(LocalDate.of(2013, 4, 1));

The reason is that LocalDate does not implement Comparable<LocalDate> but Comparable<ChronoLocalDate>. So if we go instead with the approach of @JBNizet and write with the simplified upper bound for T (just Temporal) and then use type-erasure at runtime:

public class FlexInterval<T extends Temporal> {

  ...

  @SuppressWarnings("unchecked") // code smell!
  public boolean contains(T test) {
    Comparable<T> t1 = (Comparable<T>) this.from;
    Comparable<T> t2 = (Comparable<T>) this.to;
    return (t1.compareTo(test) <= 0) && (t2.compareTo(test) >= 0);
  }
}

This code compiles. And at runtime:

FlexInterval<LocalDate> interval = 
  new FlexInterval<LocalDate>(today, today);
System.out.println(interval.contains(LocalDate.of(2013, 4, 1));
// output: false

All fine? No. A negative example demonstrates the unsafety of the new generic FlexInterval-signature (the compiler warning has its reason). If we just choose an abstract type at runtime (some users might do this in "universal" (bad) helper classes):

LocalDate today = LocalDate.now();
FlexInterval<Temporal> interval = new FlexInterval<Temporal>(today, today);
System.out.println(interval.contains(LocalDate.of(2013,4,1))); // output: false
System.out.println(interval.contains(LocalTime.now()));

... then the code compiles again, but we get:

Exception in thread "main" java.lang.ClassCastException: java.time.LocalTime can
not be cast to java.time.chrono.ChronoLocalDate
        at java.time.LocalDate.compareTo(LocalDate.java:137)
        at FlexInterval.contains(FlexInterval.java:21)

Conclusion:

The type-safety strongly requires self-referencing generics (not supported by JSR-310) AND concrete types. Since JSR-310-team has intentionally avoided generics where ever they can users willing to use JSR-310 should respect this design decision and also avoid generics in their application code. Users are best advised if they just use concrete final types, no general-purpose generified classes (which can not be completely safe).

Most important lesson: Avoid the interface Temporal in any application code.

To be noted: The hostile attitude over for generics is not my personal view. I myself can well imagine a time library which is generified. But this is another subject we don't speak about in this topic.


If it implemented Comparable<Temporal>, every suclass instance would have to be comparable with any other subclass instance. And comparing an Instant with a LocalDate, for example, doesn't make sense.

Given that the contract mandates that they are comparable, you can cast T to Comparable<T> and safely ignore the compiler warning.