Почему Temporal не расширяет Comparable в Java 8 jsr310

Документация для java.time.temporal.Temporal содержит следующее примечание:

Требования к реализации: [...] Все реализации должны быть сопоставимыми.

Почему Temporal не просто расширяет Comparable?

Фон: Я хочу работать с сопоставимыми временными интервалами (а не с подтипами типа LocalDateTime и т.д.) и прибегать к нескольким неразборчивым типом <T extends Temporal & Comparable<T>>, который также испортит функцию автозаполнения NetBeans.

Изменить: Я хочу реализовать временный интервал. Явные реализации для содержит (Interval i), содержит (Temporal t), overlaps (...), adjoins (...) и т.д. Используют Comparable:: compareTo (Comparable c) для сравнения начальной и конечной точек, но для совместимость (toDuration(), parse (CharSequence cs)) Мне нужно, например, Длительность:: между (Temporal s, Temporal e) или SubtypeOfTemporal:: parse (CharSequence cs) (дает временный).

Ответ 1

Ответ @JBNizet был очень неясным для меня на первый взгляд, потому что он советует сделать простой тип-cast до Comparable (игнорируя предупреждение компилятора), и обычно я бы предпочел код без каких-либо тиков или предупреждений (они не там просто для удовольствия), но сначала я нахожу время, чтобы исследовать все это более тщательно. Рассмотрим следующий пример простого интервала:

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);
    }
}

На этой базе (предпочтительнее OP, насколько я понял его) логично, что компилятор отклонит первую строку в следующем коде:

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

Причина в том, что LocalDate не реализует Comparable<LocalDate>, а Comparable<ChronoLocalDate>. Итак, если мы перейдем к подходу @JBNizet и напишем с упрощенной верхней границей для T (просто Temporal), а затем используем стирание стирания во время выполнения:

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);
  }
}

Этот код компилируется. И во время выполнения:

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

Все хорошо? Нет. В отрицательном примере демонстрируется небезопасность новой общей FlexInterval -сигнала (для предупреждения о компиляторе есть причина). Если мы просто выбираем абстрактный тип во время выполнения (некоторые пользователи могут делать это в "универсальных" (плохих) классах-помощниках):

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()));

... тогда код снова компилируется, но мы получаем:

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)

Вывод:

Для безопасности типа настоятельно требуются саморегуляционные дженерики (не поддерживаемые JSR-310) и конкретные типы. Поскольку команда JSR-310 намеренно избегала дженериков, где бы они ни находились, пользователи, желающие использовать JSR-310, должны уважать это дизайнерское решение, а также избегать генериков в их коде приложения. Пользователям лучше всего посоветовать, если они просто используют конкретные конечные типы, не имеющие общего назначения классы (которые не могут быть полностью безопасными).

Самый важный урок: избегайте интерфейса Temporal в любом коде приложения.

Следует отметить: враждебное отношение к дженерикам - это не мое личное мнение. Я сам могу представить себе временную библиотеку, которая является обобщенной. Но это еще одна тема, о которой мы не говорим в этой теме.

Ответ 2

Если он реализовал Comparable<Temporal>, каждый экземпляр suclass должен быть сопоставим с любым другим экземпляром подкласса. Например, сравнение Instant с LocalDate не имеет смысла.

Учитывая, что в контракте указано, что они сопоставимы, вы можете отбрасывать T до Comparable<T> и безопасно игнорировать предупреждение компилятора.

Ответ 3

Были предприняты попытки реализовать Comparable, но поскольку у Java нет универсальных генераторов, необходимо было создать Temporal под своим подтипом (например, Enum). На практике это не было хорошим компромиссом, так как в 95% + использования Temporal генерируемый параметр был бы неизвестен и, следовательно, Temporal<?>. Поскольку единственное обобщенное решение было многословным и нецелесообразным для большинства пользователей, оно не было сохранено.

Как говорит JB Nizet, вы можете просто нажать Comparable в большинстве случаев. Предоставление двух входов compareTo имеет один и тот же конкретный тип, вы не увидите никаких проблем.

В промежутках мое подозрение состоит в том, что LocalDateRange, InstantInterval и a LocalTimeInterval имеют меньше общего, чем можно представить, а обобщенное решение, вероятно, хуже, чем кодирование трех отдельных классов. Помните, что в порядке, чтобы выбрать против использования дженериков, обеспечивающих рассмотрение компромиссов.

Ответ 4

Я решил это так:

public class TemporalRange<T extends Temporal & Comparable<? super T>> implements Iterable<T>

Затем класс предоставляет Spliterator<T> Iterator<T> и Spliterator<T> в дополнение к методам stream() и parallelStream(). Я требую, чтобы тип приводил к (T) когда я использую метод plus(), но пакет java.time делает это, так что, эй.

Его можно найти здесь: https://github.com/SeverityOne/time-ext

Все еще в зачаточном состоянии на момент написания, не так много с точки зрения модульных тестов, и не проверять бесконечные циклы.