Scala:使用通配符(类型参数)和集合编译错误:“您可能希望将通配符类型调查为”

时间:2015-12-07 16:46:43

标签: scala generics type-conversion covariance type-parameter

假设我有一个名为Box的类,带有一个类型参数,它有以下实用程序方法:

class Box[T]

object Box {
    def build() : Box[_] = ???
    def combine(boxes: Set[Box[_]]) : Unit = ???
}

我试图以多种方式使用这些方法。有些人会编译,有些则不会:

// 1
Box.combine(Set(Box.build())) // compiles

// 2
val boxes : Set[Box[_]] = Set(Box.build())
Box.combine(boxes) // compiles

// 3
val boxes2 = Set(Box.build())
Box.combine(boxes2) // compile error - "type mismatch... you may wish to investigate a wildcard type as `_ <: Box`"

如果T是共变体,则所有内容都会编译。

这里发生了什么?为什么这种方式不能编译?我猜它与隐式演员有关,不是吗?

1 个答案:

答案 0 :(得分:2)

scala> val boxes2 = Set(Box.build())
boxes2: scala.collection.immutable.Set[Box[_$1]] forSome { type _$1 } = Set(Box$$anon$1@70e0accd)
"""<console>:14: warning: inferred existential type 
scala.collection.immutable.Set[Box[_$1]] forSome { type _$1 }, which
cannot be expressed by wildcards,  should be enabled
by making the implicit value scala.language.existentials visible.
This can be achieved by adding the import clause 'import      
scala.language.existentials'
or by setting the compiler option -language:existentials."""

scala> Box.combine(boxes2)
"""<console>:16: error: type mismatch;
found   : scala.collection.immutable.Set[Box[_$1]] where type _$1
required: Set[Box[_]]"""

scala> val boxes3: Set[Box[_]] = Set(Box.build())

scala> Box.combine(boxes3) // OK

编译器将存在类型推断为外部类型,因此结果对象的类型不适合combine

def check[A,B](implicit ev: A =:= B) = true

check[Set[Box[_]], Set[Box[t] forSome { type t }]] // true
check[Set[Box[_]], Set[Box[t]] forSome { type t }] // ERROR

Set[Box[t] forSome { type t }]Set[Box[t]] forSome { type t }

不同

但是,类型差异也起到了作用:

def f: Box[_] = Box[Int]()   // Box is invariant in its type arg
def g: List[_] = List[Int]() // List is covariant in its type arg

f  // Box[_] = Box()
g  // List[Any] = List() 

Set(f,f)  // ERROR: type mismatch (as Set is invariant)
Set(g,g)  // OK: scala.collection.immutable.Set[List[Any]] = Set(List())

List(f,f) // List[Box[_ >: _$1 with _$1]] forSome { type _$1; type _$1 } = List(Box(), Box())