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?

1
  • Just a small addition: I prefer to import JavaConverters and make conversions explicit with asJava. Commented Sep 30, 2013 at 13:00

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Why did they make Seq different than Set?
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.