如何使方法通用以使用LocalDate和LocalDateTime?

时间:2017-06-22 08:34:23

标签: java-8 polymorphism java-time jsr310

我有一个Java方法如下:

private static boolean isDateBetweenRange(DataSet obj, MyClass dataSource, ConditionContext context) {
    FilterContext fc = dataSource.getData();

    LocalDate dateFieldToCheck = obj.getDate(fc.getDateField()).toInstant()
                                   .atZone(ZoneId.systemDefault()).toLocalDate();
    LocalDate minDate = fc.getMinDateValue();
    LocalDate maxDate = fc.getMaxDateValue();

    if (minDate == null || maxDate == null) {
        minDate = context.getStartDate().toInstant().atZone(ZoneId.systemDefault())
                    .toLocalDate();
        maxDate = context.getEndDate().toInstant().atZone(ZoneId.systemDefault())
                    .toLocalDate();
    }

    boolean result = (dateFieldToCheck.isAfter(minDate) || dateFieldToCheck.isEqual(minDate))
            && (dateFieldToCheck.isBefore(maxDate) || dateFieldToCheck.isEqual(maxDate));

    return result;
}

我想为LocalDateTime制作相同的逻辑。如果我重载该方法,它将是LocalDateTime完全相同的代码。

如何使用Generics或任何其他机制使方法通用以与LocalDateLocalDateTime一起使用?

如何根据我的类型为context.getXXXDate()... toLocalDate()toLocalDateTime()提供公共代码?

3 个答案:

答案 0 :(得分:5)

TemporalAccessor接口可用于执行此操作。但是,请注意TemporalAccessor是一个高级接口,不应在低级实用程序代码之外使用。

boolean api(TemporalAccessor temporal1, TemporalAccessor temporal2) {
  LocalDate date1 = LocalDate.from(temporal1);
  LocalDate date2 = LocalDate.from(temporal2);
  return ...;
}

此代码现在接受LocalDateLocalDateTimeOffsetDateTimeZonedDateTime

正如评论中所提到的,仅在一个业务逻辑中调用ZoneId.systemDefault()至关重要,因为价值可能会发生变化。

答案 1 :(得分:3)

嗯...... LocalDateTimeLocalDateLocalTime的不可变组合。它有方法toLocalDate()toLocalTime(),您可以使用它们“分解”它。相反的情况也是如此 - 您也可以轻松创建合成 - 请参阅LocalDate.atTime(LocalTime)

对我来说,如果你在逻辑中考虑时间,你提取的API应该接受LocalDateTime,这似乎很自然。在可疑情况:)中,您希望通过某些原因使用LocalDate - 来提供此API,您可以明确使用,例如LocalDate.atStartOfDay。这样的东西:

boolean api(LocalDate aDate, LocalDate anotherDate)
{
    return this.api(aDate.atStartOfDay(), anotherDate.atStartOfDay());
}

boolean api(LocalDateTime aDateTime, LocalDateTime anotherDateTime)
{
    return ...;
}

虽然看起来有点难看,但仍然能很好用,不是吗?

答案 2 :(得分:0)

您可以让您的方法获得java.time.temporal.Temporal作为参数(或TemporalAccessor作为suggested by @JodaStephen)。此代码适用于两者。

我必须承认,我更喜欢像@Lachezar Balev's answer所建议的那样,但无论如何,这里有另一种选择。

由于Temporal可以是任何类型(LocalDateLocalDateTimeZonedDateTime等),但您只需要LocalDate和{{1我正在做以下事情:

  • 首先尝试创建LocalDateTime:它需要的字段多于LocalDateTime(小时/分钟/秒),因此如果可以创建它,则它是首选类型
  • 如果无法创建,我会抓住相应的例外并尝试创建LocalDate

另一个细节是我也简化了你的比较:

LocalDate

相当于:

dateFieldToCheck.isAfter(minDate) || dateFieldToCheck.isEqual(minDate)

! dateFieldToCheck.isBefore(minDate) 进行比较时进行了类似的简化:

maxDate

现在,您可以使用public boolean check(Temporal dateFieldToCheck, Temporal minDate, Temporal maxDate) { // try to get as LocalDateTime try { LocalDateTime dtCheck = LocalDateTime.from(dateFieldToCheck); LocalDateTime dtMin = LocalDateTime.from(minDate); LocalDateTime dtMax = LocalDateTime.from(maxDate); return (!dtCheck.isBefore(dtMin)) && (!dtCheck.isAfter(dtMax)); } catch (DateTimeException e) { // exception: one of the Temporal objects above doesn't have all fields required by a LocalDateTime // trying a LocaDate instead LocalDate dCheck = LocalDate.from(dateFieldToCheck); LocalDate dMin = LocalDate.from(minDate); LocalDate dMax = LocalDate.from(maxDate); return (!dCheck.isBefore(dMin)) && (!dCheck.isAfter(dMax)); } } LocalDate

来调用此方法
LocalDateTime

如果需要,您可以扩展代码以识别其他类型(例如check(LocalDate.of(2017, 6, 19), LocalDate.of(2017, 6, 18), LocalDate.of(2017, 6, 29)); // true check(LocalDate.of(2017, 6, 17), LocalDate.of(2017, 6, 18), LocalDate.of(2017, 6, 29)); // false check(LocalDateTime.of(2017, 6, 29, 9, 0), LocalDateTime.of(2017, 6, 29, 8, 0), LocalDateTime.of(2017, 6, 29, 11, 0)); // true check(LocalDateTime.of(2017, 6, 29, 7, 0), LocalDateTime.of(2017, 6, 29, 8, 0), LocalDateTime.of(2017, 6, 29, 11, 0)); // false )。

您还可以添加另一个参数,指示将使用的类型,如下所示:

LocalTime

因此,您可以使用public boolean check(Temporal dateFieldToCheck, Temporal minDate, Temporal maxDate, int type) { switch (type) { case 1: // try to get as LocalDateTime LocalDateTime dtCheck = LocalDateTime.from(dateFieldToCheck); LocalDateTime dtMin = LocalDateTime.from(minDate); LocalDateTime dtMax = LocalDateTime.from(maxDate); return (!dtCheck.isBefore(dtMin)) && (!dtCheck.isAfter(dtMax)); case 2: // trying a LocaDate instead LocalDate dCheck = LocalDate.from(dateFieldToCheck); LocalDate dMin = LocalDate.from(minDate); LocalDate dMax = LocalDate.from(maxDate); return (!dCheck.isBefore(dMin)) && (!dCheck.isAfter(dMax)); // ... and so on } // invalid type, return false? return false; } 调用它并强制它使用ZonedDateTime(然后您可以调整您的代码,并使用您从{{1}获得的对象调用此方法}}):

LocalDate

此代码也适用于不同类型:

atZone(ZoneId.systemDefault())

PS:当然您可以创建ZonedDateTime z1 = obj.getDate(fc.getDateField()).toInstant().atZone(ZoneId.systemDefault()); ZonedDateTime z2 = context.getStartDate().toInstant().atZone(ZoneId.systemDefault()); ZonedDateTime z3 = context.getEndDate().toInstant().atZone(ZoneId.systemDefault()); check(z1, z2, z3, 2); 而不是ZonedDateTime z1 = ... LocalDate d = ... LocalDateTime dt = ... // converts all to LocalDate System.out.println(check(z1, dt, d, 2)); 来定义类型。或使用Enum直接指明类型:

int