Как сделать неявный заказ на java.time.LocalDate
Я хочу использовать java.time.LocalDate
и java.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>
. Как я могу написать свой собственный неявный Orderd для использования <, <=,>,> = операторов/методов для сравнения 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 для всех классов Java, который реализует Comparable<T>
. Вам просто нужно 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
реализует LocalDate
Comparable<ChronoLocalDate>
вместо Comparable<LocalDate>
. Я не мог найти способ изменить вышеупомянутый неявный упорядоченный метод, чтобы он соответствовал LocalDate
/Comparable<ChronoLocalDate>
. Любая идея?
Ответы
Ответ 1
Вы можете использовать Ordering.by
, чтобы создать упорядочение для любого типа, задав функцию из этого типа на то, что уже имеет заказ - в этом случае - Long
:
implicit val localDateOrdering: Ordering[LocalDate] = Ordering.by(_.toEpochDay)
Ответ 2
Сравнение по LocalDate.toEpochDay
понятно, хотя, может быть, и относительно медленно...
Ответ @tzach-zohar великолепен тем, что наиболее очевидно, что происходит; вы заказываете к дню эпохи:
implicit val localDateOrdering: Ordering[LocalDate] = Ordering.by(_.toEpochDay)
Однако, если вы посмотрите на реализацию toEpochDay
то увидите, что он относительно задействован - 18 строк кода, 4 деления, 3 условия и вызов isLeapYear()
- и результирующее значение не кэшируется, поэтому он получает пересчитывается при каждом сравнении, что может быть дорогостоящим, если будет отсортировано большое количество LocalDate
.
... использование LocalDate.compareTo
, вероятно, более производительным...
Реализация LocalDate.compareTo
более проста - всего 2 условия, без деления - и это то, что вы получите при неявном преобразовании java.lang.Comparable
scala.math.Ordering
с 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])
... который позволяет вам упорядочить LocalDate
их к ChronoLocalDate
s и используя Ordering[ChronoLocalDate]
который scala.math.Ordering.Implicits._
дает scala.math.Ordering.Implicits._
!
... и, в конце концов, лучше всего выглядит с лямбда-синтаксисом для типов SAM
Лямбда-синтаксис для типов SAM, представленный в Scala 2.12, может сделать очень короткую работу по созданию new Ordering
:
implicit val localDateOrdering: Ordering[LocalDate] = _ compareTo _
... и я думаю, что это становится моим личным фаворитом! Краткий, все еще довольно ясный и использующий (я думаю) самый эффективный метод сравнения.
Ответ 3
Вот решение, которое я использую:
определить два имплицита. Первый для создания Ordering[LocalDate]
. И второй способ дать метод LocalDate
a 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
Ответ 4
Вот мое решение для java.time.LocalDateTime
implicit val localDateTimeOrdering: Ordering[LocalDateTime] =
Ordering.by(x => x.atZone(ZoneId.of("UTC")).toEpochSecond)
Ответ 5
Небольшое изменение неявного 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
.