2

I have the following:

    class Goo
    trait Bar[D] {
     def toD : D
    }
    class Moo extends Bar[Goo] {
      def toD : Goo = new Goo
    }

    object Foo {
      import scala.collection.JavaConversions._
      import java.util

      implicit def scalaSetToJavaSet[D](bars: Set[Bar[D]]) : util.Set[D] = bars.map(_.toD)
    }  

    trait Zoo {
      import java.util
      import Foo._

      var javaSet : util.Set[Goo] = Set(new Moo) //compile error

      var explicitJavaSet: util.Set[Goo] = scalaSetToJavaSet(Set(new Moo)) //works!!!
    }

When I try to compile this code I get an error:

"error: type mismatch;
   found   : scala.collection.immutable.Set[Moo]
   required: java.util.Set[Goo]
           var javaSet : util.Set[Goo] = ***Set(new Moo)***"

The explicit definition compiles. Why doesn't the implicit conversion work?

netta
  • 508
  • 6
  • 16
  • Just a small addition: I prefer to import JavaConverters and make conversions explicit with `asJava`. – Markus Sep 30 '13 at 13:00

2 Answers2

7

This is indeed tricky. Basically your idea is correct. You define a conversion method scalaSetToJavaSet that is imported. The problem comes from the fact that (Scala's) Set[A] is invariant in its type parameter A. That means that Set[Moo] is not a sub-type of Set[Bar[_]].

In order to fix that, you need to tell the implicit conversion that the set element can be a sub-type of bar:

implicit def scalaSetToJavaSet[D, B <: Bar[D]](bars: Set[B]): java.util.Set[D] = 
  bars.map(_.toD)

val javaSet: java.util.Set[Goo] = scala.Set(new Moo)  // ok

You can read this now as: Given a type D and some sub-type of Bar whose type parameter is D, conduct the conversion. This method is now called as scalaSetToJavaSet[Goo, Moo] (and not scalaSetToJavaSet[Goo, Bar[Goo]]).


As the others have pointed out, it is easier to use a ready-made conversion from JavaConversions instead of writing your own.

0__
  • 66,707
  • 21
  • 171
  • 266
1

Firstly implicit function scalaSetToJavaSet does not work because it expects Set[Bar[D]] and you are passing Set[Moo]. This is because type parameter of Set is invariant and not co-variant (It would had worked in case of List).

So a short answer is

import scala.collection.JavaConversions._
var javaSet : util.Set[Moo] = Set(new Moo)

Or you can change your declaration to:

implicit def scalaSetToJavaSet[D,T <:Bar[D]](bars: Set[T]) : util.Set[D] = bars.map(_.toD)

var javaSet : util.Set[Goo] = Set(new Moo)

Now it says that you can pass any type which extends Bar[D] and hence works with Moo

Jatin
  • 31,116
  • 15
  • 98
  • 163