为什么Temporal不能在Java 8 jsr310中扩展Comparable

时间:2014-05-26 13:10:30

标签: java java-8 java-time

java.time.temporal.Temporal的文档包含以下注释:

  

实施要求:[...]所有实施必须是可比较的。

为什么Temporal不仅仅扩展Comparable?

背景:我想使用类似的时态(不是像LocalDateTime等子类型),并且不得不求助于某种难以辨认的类型<T extends Temporal & Comparable<T>>,这也会混淆NetBeans&#39;自动完成功能。

编辑:我想实现一个时间间隔。包含(Interval i),包含(Temporal t),重叠(...),adjoins(...)等的明显实现使用Comparable :: compareTo(Comparable c)来比较起点和终点,但是我需要的互操作性(toDuration(),parse(CharSequence cs)) Duration :: between(Temporal s,Temporal e)或SubtypeOfTemporal :: parse(CharSequence cs)(产生时间)。

4 个答案:

答案 0 :(得分:5)

如果它实现了Comparable<Temporal>,则每个suclass实例都必须与任何其他子类实例相当。例如,将Instant与LocalDate进行比较是没有意义的。

鉴于合同要求它们具有可比性,您可以将T强制转换为Comparable<T>并安全地忽略编译器警告。

答案 1 :(得分:5)

尝试实现Comparable,但由于Java没有自我类型泛型,因此必须使Temporal通过其子类型(如Enum)进行泛化。在实践中,这不是一个很好的权衡,因为在Temporal的95%+使用中,通用参数将是未知的,因此Temporal<?>。由于唯一的一般化解决方案对大多数用户来说都是冗长且不切实际的,因此没有保留。

正如JB Nizet的回答所说,在大多数情况下你可以转换为Comparable。提供compareTo的两个输入具有相同的具体类型,您应该看到没有问题。

每隔一段时间,我怀疑LocalDateRangeInstantIntervalLocalTimeInterval的共同点比想象的要少,而且一般的解决方案可能比编写三个单独的类更糟糕。请记住,可以选择不使用泛型,只要考虑到权衡因素。

答案 2 :(得分:3)

乍一看@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);
    }
}

在那个基础上(就我所理解的而言,首选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签名的不安全(编译器警告有其原因)。如果我们只是在运行时选择一个抽象类型(某些用户可能在&#34;通用&#34;(坏)帮助器类中执行此操作):

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

需要注意的是:对仿制药的敌对态度不是我个人的看法。我自己可以想象一个时间库,这是一个普遍的。但这是我们在这个主题中没有谈到的另一个主题。

答案 3 :(得分:0)

我这样解决了:

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

然后,该类除了提供Iterator<T>Spliterator<T>方法之外,还提供stream()parallelStream()。使用(T)方法时,我需要将类型强制转换为plus(),但是java.time包也是如此,所以。

可以在这里找到:https://github.com/SeverityOne/time-ext

在撰写本文时仍处于起步阶段,在单元测试方面还不多,并且没有针对无穷循环的检查。