Ren*_*ené 10 java java-8 java-time
java.time.temporal.Temporal的文档包含以下注释:
实施要求:[...]所有实施必须是可比较的.
为什么Temporal不仅仅扩展可比性?
背景:我想使用类似的时间(不是像LocalDateTime等子类型),并且必须采用一种有点难以辨认的类型<T extends Temporal & Comparable<T>>
,这也会混淆NetBeans的自动完成功能.
编辑:我想实现一个时间间隔.包含(Interval i),包含(Temporal t),重叠(...),adjoins(...)等的明显实现使用Comparable :: compareTo(Comparable c)来比较起点和终点,但是互操作性(toDuration(),parse(CharSequence cs))我需要例如Duration :: between(Temporal s,Temporal e)或SubtypeOfTemporal :: parse(CharSequence cs)(产生Temporal).
如果实现Comparable<Temporal>
,则每个suclass实例都必须与任何其他子类实例相当.例如,将Instant与LocalDate进行比较是没有意义的.
鉴于合同要求它们具有可比性,您可以T
转换Comparable<T>
并安全地忽略编译器警告.
尝试实现Comparable
,但由于Java没有自我类型泛型,因此必须Temporal
通过其子类型(如Enum
)进行泛化.在实践中,这不是一个很好的权衡,因为在95%以上的使用中Temporal
,生成的参数将是未知的,因此Temporal<?>
.由于唯一的一般化解决方案对大多数用户来说是冗长且不切实际的,因此没有保留.
正如JB Nizet的回答所说,你可以Comparable
在大多数情况下进行投射.提供两个输入compareTo
具有相同的具体类型,您应该看到没有问题.
有一段时间,我怀疑a LocalDateRange
,a InstantInterval
和a的LocalTimeInterval
共同点比想象的要少,而且一般化的解决方案可能比编写三个单独的类更糟糕.请记住,可以选择不使用泛型,只要考虑到权衡.
乍一看,@JBNizet 的答案对我来说非常模糊,因为他建议对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);
}
}
Run Code Online (Sandbox Code Playgroud)
在此基础上(据我了解,OP 首选)编译器将拒绝以下代码中的第一行是合乎逻辑的:
FlexInterval<LocalDate> interval =
new FlexInterval<LocalDate>(today, today); // compile-error
System.out.println(interval.contains(LocalDate.of(2013, 4, 1));
Run Code Online (Sandbox Code Playgroud)
原因是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);
}
}
Run Code Online (Sandbox Code Playgroud)
此代码编译。在运行时:
FlexInterval<LocalDate> interval =
new FlexInterval<LocalDate>(today, today);
System.out.println(interval.contains(LocalDate.of(2013, 4, 1));
// output: false
Run Code Online (Sandbox Code Playgroud)
一切都好吗?不是。一个反面例子证明了新的泛型签名的不安全性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()));
Run Code Online (Sandbox Code Playgroud)
...然后代码再次编译,但我们得到:
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)
Run Code Online (Sandbox Code Playgroud)
结论:
类型安全强烈要求自引用泛型(JSR-310 不支持)和具体类型。由于 JSR-310 团队有意避免使用泛型,他们愿意使用 JSR-310 的用户应该尊重这个设计决定,并在他们的应用程序代码中避免使用泛型。最好建议用户只使用具体的 final 类型,而不是通用的泛型类(这不可能是完全安全的)。
最重要的一课:避免Temporal
在任何应用程序代码中使用接口。
需要注意的是:对泛型的敌意不是我个人的观点。我自己可以很好地想象一个泛型的时间库。但这是我们在本主题中不讨论的另一个主题。
归档时间: |
|
查看次数: |
1527 次 |
最近记录: |