Translate common wildcards from Java to Scala

In the java.util.Collections class, we have two variants of the sort method, which accepts a list of arbitrary objects with the corresponding Comparator :

 public static <T> void sort(List<T> list, Comparator<? super T> comparator) 

And one that accepts a list of Comparable objects:

 public static <T extends Comparable<? super T>> void sort(List<T> list) 

I was thinking how to translate such restricted method wildcards into Scala. For the first version, I translated the signature literally and at a glance without compilation problems:

 def sort[T](list: List[T], comparator: Comparator[_ >: T]) { ??? } 

But then I found that I could not call this method with the following arguments:

 val comparator = new Comparator[Object] { def compare(o1: Object, o2: Object) = ??? } val list = new ArrayList[Number] sort[Object](list, comparator) 

The last line gives this compilation error, although I explicitly specify the type T as Object .

type mismatch; found: java.util.ArrayList [Number] required: java.util.List [Object] Note: Number <: Object, but the Java-defined list of attributes is invariant in type E. You can examine the type of wildcards, for example _ <: Object . (SLS 3.2.10)

In fact, I found out that it is not even possible to call a single Java method directly, since it fails with the same type of errors.

 Collections.sort[Object](list, comparator) 

As for the version with a comparable list, I came up with this announcement:

 def sort[T <: Comparable[_ >: T]](list: List[T]) { ??? } 

But this does not work at all:

illegal circular reference including type T


What am I doing wrong? Are Scala varieties of generics following various Java rules? How to call one call to the Collections.sort method without receiving a compilation error?

Side note:

No, I do not ask how to sort the list in Scala. I know that Scala has its own collection of collections, sorting functions, and a different approach to comparing objects (e.g. Ordered and Ordering ). My question is about the general problem of generic methods and translating generics from Java to Scala.

+6
source share
2 answers

You specify the wrong type parameter for T : you sort List[Number] , not List[Object] :

 sort[Number](list, comparator) 

will work.

If you want to call sorting without a type argument, you need to define two lists of arguments (due to the way type-output works in Scala):

 def sort[T](list: List[T])(comparator: Comparator[_ >: T]) { ??? } // Then sort(list)(comparator) 

You might want to consider Scala types that have proper covariance support (that is, Scala a List[Number] has a List[Object] ).

As for the version with a comparable one, you have to explicitly write a template:

 def sort[T <: Comparable[T], U <: T](list: List[U]) { ??? } 
+4
source

You can name a Java variant (or yours):

 Collections.sort[Number](list, comparator) 

The problem here is that Java types are invariant. In other words, this does not work in Java:

 List<Number> l1; List<Integer> l2 = l1; //contravariance fails List<Object> l3 = l1; //covariance fails 

In Scala, generic type parameters can be declared covariant or contravariant in their declaration. Scala A list type parameter is declared covariant (works because it is immutable). In other words, it really is:

 val l1: List[Number] = ??? val l2: List[Object] = l1 //valid 

But since you are using Java java.util.List , this is not an option.

+3
source

Source: https://habr.com/ru/post/944489/


All Articles