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