2

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<?>?

Lopotun
  • 611
  • 7
  • 15
  • 1
    The problem is not converting the **Scala** `List` into the **Java** one. You already have that. The problem is that because **Java** doesn't provide control over _variance_ a `j.u.List` is not a subtype of a `j.u.List>`. You may try the up casting before the call to `asJava` something like this should work: `val bars = (List(bar) : List[String]).asJava` – Luis Miguel Mejía Suárez Feb 05 '20 at 11:56
  • 1
    You don't have any `java.util.List>` in your code or error. – Jasper-M Feb 05 '20 at 13:39
  • @LuisMiguelMejíaSuárez Unfortunately, it ends up with "Cannot upcast List[Bar] to List[String]" compilation error. And yes, it makes sense: indeed, a list of Bar is not a list of String. – Lopotun Feb 05 '20 at 14:20
  • @Lopotun O, sorry a typo, I was in mobile. It should have been `val bars = (List(bar) : List[Foo[String]]).asJava` Because a **Bar** is **Foo[String]**. – Luis Miguel Mejía Suárez Feb 05 '20 at 14:26
  • @Jasper-M I'm afraid I didn't quite understand your answer. The given Java code is legacy and I cannot change it. – Lopotun Feb 05 '20 at 16:38
  • @LuisMiguelMejíaSuárez thanks for your answer. Now there is the the same problem but now on the construction level: Error:(44, 18) type mismatch; found : java.util.List[sof.Foo[String]] required: java.util.List[sof.Foo[\_]] Note: sof.Foo[String] <: 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) – Lopotun Feb 06 '20 at 07:32

1 Answers1

3

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)
Jasper-M
  • 14,966
  • 2
  • 26
  • 37
Tim
  • 26,753
  • 2
  • 16
  • 29
  • Same (almost) error. Now it won't cast sof.Foo[String] to sof.Foo[_] Error:(45, 38) type mismatch; found : java.util.List[sof.Foo[String]] required: java.util.List[sof.Foo[_]] Note: sof.Foo[String] <: 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) val holder = new sof.Holder(foos.asJava) – Lopotun Feb 05 '20 at 14:39
  • 1
    @Lopotun Are you sure you have `foos: List[Foo[_]]` and not `foos: List[Foo[String]]`? That would produce the error message you show. – Alexey Romanov Feb 05 '20 at 18:10
  • @AlexeyRomanov Bingo! Yes, you're right. It was my typos and List[Foo[\_]] works perfectly. Now, it's my turn to understand why and how it works :-) – Lopotun Feb 06 '20 at 07:49
  • @Lopotun It wasn't your typo, the answer was updated after your first comment so you were correct at the time. Jasper-M helpfully updated the answer to the correct one after your comment. – Tim Feb 06 '20 at 07:53
  • @Tim Yes, I see it now. It works and it's the main thing. And I learned that I should revise answers: they may be changed :-) – Lopotun Feb 06 '20 at 08:11