I have the following Scala code:
import java.util.{Comparator, function}
object ComparatorTest {
Comparator.comparing[Int, String](new function.Function[Int, String] {
override def apply(t: Int): String = t.toString
})
}
which compiles without issue. I think I should be able to replace that Function
with a lambda (that's what I understand from the doc, at least, and IntelliJ is of the same opinion). However, when I replace it with
Comparator.comparing[Int, String]((t: Int) ⇒ t.toString)
I get a compiler error:
Error:(6, 23) overloaded method value comparing with alternatives:
(x$1: java.util.function.Function[_ >: Int, _ <: String])java.util.Comparator[Int] <and>
(x$1: java.util.function.Function[_ >: Int, _ <: String],x$2: java.util.Comparator[_ >: String])java.util.Comparator[Int]
cannot be applied to (Int => String)
Comparator.comparing[Int, String]((t: Int) ⇒ t.toString)
(It looks to me like the first alternative should match.)
I've just asked a similar question, and the solution was to explicitly specify type parameters, but in this case, I think I've specified everything I could. Is there another solution to use a lambda, or is an explicit Function
needed here? If the latter, is there a doc that explains when exactly a lambda can be substituted for a SAM?
TL;DR: Use Ordering
rather than using Comparator
directly.
You can use type ascription to force it to recognize your argument as a java.util.function.Function[Int, String]
:
Comparator.comparing[Int, String]((_.toString) : function.Function[Int, String])
Note that the parentheses around _.toString
are required; otherwise, it tries to apply the ascription to the expression in the lambda rather than the lambda as a whole. This code is basically equivalent to:
val tmp: function.Function[Int, String] = _.toString
Comparator.comparing[Int, String](tmp)
However, when writing Scala, is generally better to use Ordering
. Since Ordering
extends Comparator
, it can be used anywhere a Comparator
is expected. You could use it like:
val cmp: Comparator[Int] = Ordering.by(_.toString)
or:
val cmp = Ordering.by[Int, String](_.toString)
As you can see, the type inference works far better here.
If the reason for using Comparator
instead of Ordering
is thenCompare
, that is easily overcome with an implicit:
object PimpOrdering {
implicit class OrderOrElse[T](val o: Ordering[T]) extends AnyVal {
def orElse(o2: Ordering[T]) = new Ordering[T] {
def compare(a: T, b: T) = {
val cmp = o.compare(a,b)
if (cmp == 0) o2.compare(a,b) else cmp
}
}
def orElse[S : Ordering](f: T => S) = orElse(Ordering.by(f))
}
}
Now you can write things like
Ordering.by[Foo, String](_.foo).orElse(_.bar).orElse(_.baz)
etc
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With