Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala resolving to wrong override in Comparator.thenComparing

I'm trying to translate the following Java code:

import java.util.Comparator;

public class ComparatorTestJava {
    public static void test() {
        Comparator<String> cmp = (s1, s2) -> 0;
        cmp = cmp.thenComparing(s -> s);
    }
}

into Scala. I think this should work:

import java.util.{Comparator, function}

object ComparatorTest {
  var comparator: Comparator[String] = (t1, t2) ⇒ 0
  comparator = comparator.thenComparing(new function.Function[String, String] {
    override def apply(t: String): String = t
  })
}

But it fails with the following error:

Error:(7, 41) type mismatch;
 found   : java.util.function.Function[String,String]
 required: java.util.Comparator[? >: String]
  comparator = comparator.thenComparing(new function.Function[String, String] {

It looks like the Scala compiler is convinced I'm trying to use thenComparing(Comparator) instead of thenComparing(Function). Is there any way I can tell it which it is? Or is that actually not the issue?

(I realize there are other, perhaps more idiomatic, ways to do build a comparator in Scala, but I'm interested in understanding why this fails.)

like image 619
zale Avatar asked Feb 09 '19 19:02

zale


1 Answers

Given the definitions

val comparator: Comparator[String] = (t1, t2) => 0
val f: function.Function[String, String] = s => s

the following fails with the same error as in your question:

comparator.thenComparing(f)

but this compiles successfully:

comparator.thenComparing[String](f)

This is a very common type of errors that occur regularly whenever one tries to use generic Java interfaces from Scala. The reason is that Java's use-site variance does not play well with Scala's declaration-site variance, so that Java's Comparator<? super T> translates into a wildcard type Comparator[_ >: T], and it somehow confuses the type inference algorithm (especially if you combine it with overloaded methods and SAM). However, once recognized, the problem is very easily solved by specifying the type parameters explicitly, in this case, adding an explicit [String] is enough.

like image 59
Andrey Tyukin Avatar answered Sep 23 '22 14:09

Andrey Tyukin