Je veux utiliser Java.time.LocalDate
et Java.time.LocalDateTime
avec un Ordered
implicite comme:
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._
ne fonctionne pas, car LocalDate
hérite de Comparable<ChronoLocalDate>
au lieu de Comparable<LocalDate>
. Comment puis-je écrire mon propre Orderd imlicit pour utiliser <, <=,>,> = opérateurs/méthodes afin de comparer les LocalDate
?
Modifier:
J'ai trouvé un moyen d'utiliser une classe implicite:
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")
Mais je préférerais une méthode comme celle de Scala pour toutes les classes Java qui implémente Comparable<T>
. Il vous suffit de import scala.math.Ordering.Implicits._
dans votre code. Scala l'implémente comme:
implicit def ordered[A <% Comparable[A]]: Ordering[A] = new Ordering[A] {
def compare(x: A, y: A): Int = x compareTo y
}
Malheureusement, LocalDate
implémente Comparable<ChronoLocalDate>
au lieu de Comparable<LocalDate>
. Je ne pouvais pas trouver un moyen de modifier la méthode ordonnée implicite ci-dessus pour l'adapter à LocalDate
/Comparable<ChronoLocalDate>
. Une idée?
Vous pouvez utiliser Ordering.by
pour créer un ordre pour n’importe quel type, en donnant une fonction de ce type à quelque chose qui a déjà un ordre - dans ce cas, à Long
:
implicit val localDateOrdering: Ordering[LocalDate] = Ordering.by(_.toEpochDay)
LocalDate.toEpochDay
est clair, bien que peut-être relativement lent ...Le réponse de @ tzach-zohar est génial, en ce sens que ce qui se passe est plus évident; vous commandez par Epoch Day:
implicit val localDateOrdering: Ordering[LocalDate] = Ordering.by(_.toEpochDay)
Cependant, si vous regardez l’implémentation de toEpochDay
, vous verrez qu’elle est relativement impliquée - 18 lignes de code, avec 4 divisions, 3 conditions et un appel à isLeapYear()
- et la valeur résultante n’est pas mise en cache. Ainsi, il est recalculé à chaque comparaison, ce qui peut coûter cher s’il ya un grand nombre de LocalDate
s à trier.
LocalDate.compareTo
est probablement plus performant ...Le implémentation de LocalDate.compareTo
est plus simple - 2 conditions seulement, pas de division - et vous obtiendrez ce que vous obtiendriez avec cette conversion implicite de Java.lang.Comparable
EN scala.math.Ordering
que scala.math.Ordering.Implicits._
offre, si seulement cela fonctionnait ! Mais comme vous l'avez dit, ce n'est pas le cas, car LocalDate
hérite de Comparable<ChronoLocalDate>
au lieu de Comparable<LocalDate>
. Une façon d'en tirer parti pourrait être ...
import scala.math.Ordering.Implicits._
implicit val localDateOrdering: Ordering[LocalDate] =
Ordering.by(identity[ChronoLocalDate])
... qui vous permet de commander LocalDate
s en les convertissant en ChronoLocalDate
s, et en utilisant le Ordering[ChronoLocalDate]
que scala.math.Ordering.Implicits._
vous donne!
La syntaxe lambda pour les types SAM , introduite avec Scala 2.12, permet de créer un new Ordering
très rapidement:
implicit val localDateOrdering: Ordering[LocalDate] = _ compareTo _
... et je pense que cela finit par être mon préféré! Concis, toujours assez clair, et utilisant (je pense) la méthode de comparaison la plus performante.
Voici la solution que j'utilise:
définir deux implications. Le premier pour rendre un Ordering[LocalDate]
disponible. Et une seconde pour donner à LocalDate
une méthode compare
qui s'avère très utile. En règle générale, je les mets dans des objets de package d'une bibliothèque. Je peux simplement les inclure où j'en ai besoin.
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)
}
}
avec les deux définis, vous pouvez maintenant faire des choses comme:
import net.fosdal.oslo.odatetime._
val bool: Boolean = localDate1 < localDate1
val localDates: Seq[LocalDate] = ...
val sortedSeq = localDates.sorted
Alternativement ... vous pouvez simplement utiliser ma bibliothèque (v0.4.3) directement. voir: https://github.com/sfosdal/oslo
Une légère modification de la variable ordered
implicite devrait suffire.
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
}
Maintenant, tout type qui est comparable à lui-même ou à un supertype de lui-même devrait avoir une Ordering
.