如何在java.time.LocalDate上进行隐式Ordered

问题描述 投票:14回答:5

我想使用java.time.LocalDatejava.time.LocalDateTime与隐含的Ordered像:

val date1 = java.time.LocalDate.of(2000, 1, 1)
val date2 = java.time.LocalDate.of(2010, 10, 10)
if (date1 < date2) ...

import scala.math.Ordering.Implicits._不起作用,因为LocalDate继承自Comparable<ChronoLocalDate>而不是Comparable<LocalDate>。我怎样才能编写自己的Imred使用,> =运算符/方法来比较LocalDate的?

编辑:

我找到了一种使用隐式类的方法:

import java.time.{LocalDate}

object MyDateTimeUtils {
  implicit class MyLocalDateImprovements(val ld: LocalDate)
           extends Ordered[LocalDate] {
    def compare(that: LocalDate): Int = ld.compareTo(that)
  }
}

// Test

import MyDateTimeUtils._

val d1 = LocalDate.of(2016, 1, 1)
val d2 = LocalDate.of(2017, 2, 3)

if (d1 < d2) println("d1 is less than d2")

但我更喜欢像Scala那样为所有实现Comparable<T>的Java类做的事情。你只需要在你的代码中使用import scala.math.Ordering.Implicits._。 Scala实现它像:

implicit def ordered[A <% Comparable[A]]: Ordering[A] = new Ordering[A] {
  def compare(x: A, y: A): Int = x compareTo y
}

但不幸的是LocalDate实施Comparable<ChronoLocalDate>而不是Comparable<LocalDate>。我找不到修改上面的隐式有序方法以适应LocalDate / Comparable<ChronoLocalDate>的方法。任何的想法?

scala implicit java-time localdate
5个回答
18
投票

您可以使用Ordering.by为任何类型创建排序,给定从该类型到已经具有排序的函数的函数 - 在本例中为Long

implicit val localDateOrdering: Ordering[LocalDate] = Ordering.by(_.toEpochDay)

11
投票

Comparing by LocalDate.toEpochDay is clear, though maybe relatively slow...

answer by @tzach-zohar很棒,因为它最明显的是什么;你在大纪元日订购:

implicit val localDateOrdering: Ordering[LocalDate] = Ordering.by(_.toEpochDay)

但是,如果你看一下implementation of toEpochDay,你会发现它相对复杂 - 18行代码,4个分区,3个条件和isLeapYear()调用 - 结果值没有缓存,所以每次比较都会重新计算,如果要对大量的LocalDates进行分类,这可能会很昂贵。

...making use of LocalDate.compareTo is probably more performant...

implementation of LocalDate.compareTo更简单 - 只有2个条件,没有分区 - 这就是java.lang.Comparable to scala.math.Ordering提供的scala.math.Ordering.Implicits._的隐式转换所带来的,只要它有效!但正如你所说,它没有,因为LocalDate继承自Comparable<ChronoLocalDate>而不是Comparable<LocalDate>。利用它的一种方法可能是......

import scala.math.Ordering.Implicits._

implicit val localDateOrdering: Ordering[LocalDate] =
  Ordering.by(identity[ChronoLocalDate])

...这可以让你通过将它们投射到LocalDates来订购ChronoLocalDates,并使用Ordering[ChronoLocalDate]给你的scala.math.Ordering.Implicits._

...and in the end it looks best with the lambda syntax for SAM types

与Scala 2.12一起引入的lambda syntax for SAM types可以完成构建new Ordering的非常简短的工作:

implicit val localDateOrdering: Ordering[LocalDate] = _ compareTo _

......我认为这最终成为我个人的最爱!简洁,仍然相当清楚,并使用(我认为)最佳表现的比较方法。


3
投票

这是我使用的解决方案:

定义两个含义。第一个使Ordering[LocalDate]可用。第二个给LocalDate一个compare方法非常方便。我通常将它们放在库中的包对象中,我可以只包括我需要它们的地方。

package object net.fosdal.oslo.odatetime {

  implicit val orderingLocalDate: Ordering[LocalDate] = Ordering.by(d => (d.getYear, d.getDayOfYear))

  implicit class LocalDateOps(private val localDate: LocalDate) extends AnyVal with Ordered[LocalDate] {
    override def compare(that: LocalDate): Int = Ordering[LocalDate].compare(localDate, that)
  }
}

通过这两个定义,您现在可以执行以下操作:

import net.fosdal.oslo.odatetime._

val bool: Boolean = localDate1 < localDate1

val localDates: Seq[LocalDate] = ...
val sortedSeq = localDates.sorted

或者......你可以直接使用我的库(v0.4.3)。见:https://github.com/sfosdal/oslo


1
投票

这是我对java.time.LocalDateTime的解决方案

implicit val localDateTimeOrdering: Ordering[LocalDateTime] =
  Ordering.by(x => x.atZone(ZoneId.of("UTC")).toEpochSecond)

0
投票

对隐含的ordered稍作修改应该可以解决问题。

type AsComparable[A] = A => Comparable[_ >: A]

implicit def ordered[A: AsComparable]: Ordering[A] = new Ordering[A] {
  def compare(x: A, y: A): Int = x compareTo y
}

现在,每种与自身或超自身类型相当的类型都应该有一个Ordering

© www.soinside.com 2019 - 2024. All rights reserved.