从scala设置到Java Set的隐式转换

时间:2013-09-30 12:07:51

标签: scala implicit-conversion

我有以下内容:

    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!!!
    }

当我尝试编译此代码时出现错误:

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

显式定义编译。为什么隐式转换不起作用?

2 个答案:

答案 0 :(得分:7)

这确实很棘手。基本上你的想法是正确的。您可以定义导入的转换方法scalaSetToJavaSet。问题来自于(Scala)Set[A]在其类型参数A中不变的事实。这意味着Set[Moo]不是Set[Bar[_]]的子类型。

为了解决这个问题,你需要告诉隐式转换,set元素可以是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

您现在可以这样理解:给定类型D和类型参数为Bar的某些子类型D,进行转换。此方法现在称为scalaSetToJavaSet[Goo, Moo](而不是scalaSetToJavaSet[Goo, Bar[Goo]])。


正如其他人所指出的那样,使用JavaConversions的现成转换更容易,而不是自己编写。

答案 1 :(得分:1)

首先隐式函数scalaSetToJavaSet不起作用,因为它期望Set[Bar[D]]并且您正在传递Set[Moo]。这是因为Set的类型参数是不变的而不是共变体(如果是List,它会起作用)。

所以简短的回答是

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

或者您可以将声明更改为:

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

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

现在它说你可以传递任何扩展Bar[D]的类型,因此可以使用Moo