Search code examples
javascalagenericsbounded-wildcard

How to convert Scala List[String] to legacy Java List<?>


I have legacy (no modification allowed) Java code:

//File Foo.java
package sof;
public interface Foo<T> {}

//File Bar.java
package sof;
public class Bar implements Foo<String> {}

//File Holder.java    
package sof;
import java.util.List;
public class Holder {
    private List<Foo<?>> lst;
    public Holder() {}
    public Holder(List<Foo<?>> lst) {this.lst = lst;}
    public List<Foo<?>> getLst() {return lst;}
    public void setLst(List<Foo<?>> lst) {this.lst = lst;}
}

Now, in my Scala code I want to pass List of bars to holder:

import sof._
import collection.JavaConverters._
val bar = new Bar()
val bars: java.util.List[Bar] = List(bar).asJava
val holder = new sof.Holder(bars)

It won't compile. The error is:

Error:(37, 18) type mismatch; found : java.util.List[sof.Bar] required: java.util.List[sof.Foo[_]] Note: sof.Bar <: sof.Foo[_], but Java-defined trait List is invariant in type E. You may wish to investigate a wildcard type such as _ <: sof.Foo[_]. (SLS 3.2.10)
new sof.Holder(bars)

As a workaround, I can

val holder = new sof.Holder()
val tmpLst = holder.getLst
tmpLst.add(bar)

It works but it's awful. So, what's the Scala structure that fits java.util.List<?>?


Solution

  • Java wants java.util.List<Foo<?>> but you are giving it java.util.List<Bar>. Java does not support List variance so this does not work.

    You need to give it the actual type it wants:

    val bar = new Bar()
    val foos: List[Foo[_]] = List(bar)
    val holder = new sof.Holder(foos.asJava)