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



为什么Temporal不只是扩展可比性?

背景:我想使用可比较的时态(而不是像LocalDateTime等子类型),并且不得不诉诸于某种难以辨认的<T extends Temporal & Comparable<T>>类型,它也弄乱了NetBeans的自动完成功能。

编辑:我想实现一个时间间隔。 contains(Interval i),contains(Temporal t),overlaps(...),adjoins(...)等的明显实现是使用Comparable::compareTo(Comparable c)比较起点和终点,但对于互操作性(toDuration(),parse(CharSequence cs))我需要例如持续时间::介于(Temporal s,Temporal e)或SubtypeOfTemporal::parse(CharSequence cs)(产生Temporal)。

最佳答案

乍一看,@ 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首选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上限(只是临时的)编写,然后在运行时使用类型擦除:
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 -signature的不安全(编译器警告有其原因)。如果我们仅在运行时选择抽象类型(某些用户可能会在“通用”(不良)帮助程序类中执行此操作):
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接口(interface)。

需要指出的是:对仿制药的敌对态度不是我个人的看法。我自己可以很好地想象一个时间库,它被泛化了。但这是我们在本主题中未提及的另一主题。

07-24 09:28