带有通配符类型参数的map上的flatMap

txt*_*low 4 generics types scala scala-collections

我想写这样的东西:

trait Typed[T]

trait Test {

  def testMap: Map[Typed[_], Int]

  def test = testMap.flatMap {case (typed, size) => Seq.fill(size)(typed)}
}
Run Code Online (Sandbox Code Playgroud)

但是我收到以下错误:

error: no type parameters for method flatMap: (f: ((Typed[_], Int)) => Traversable[B])(implicit bf: scala.collection.generic.CanBuildFrom[scala.collection.immutable.Map[com.quarta.service.querybuilder.Typed[_],Int],B,That])That exist so that it can be applied to arguments (((Typed[_], Int)) => Seq[Typed[_0]] forSome { type _0 })
--- because ---
argument expression's type is not compatible with formal parameter type;
found   : ((Typed[_], Int)) => Seq[Typed[_0]] forSome { type _0 }
required: ((Typed[_], Int)) => Traversable[?B]
def test = testMap.flatMap {case (typed, size) => Seq.fill(size)(typed)}
Run Code Online (Sandbox Code Playgroud)

如果将testMap类型更改为:此代码有效:

def testMap: Map[Typed[Any], Int]
Run Code Online (Sandbox Code Playgroud)

有什么区别以及如何解决我的问题?

fot*_*ton 5

如果我正确地理解了你的问题,答案是:如果Typed是协变的T,你可以这样做,即trait Typed[+T].

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Typed[+T: Manifest] {
  override def toString = "Typed[" + implicitly[Manifest[T]].toString + "]"
}

trait Test {
  def testMap: Map[Typed[_], Int]

  def foo = testMap flatMap { case (t, s) => Seq.fill(s)(t) }
}

val bar = new Test { 
  def testMap = Map(new Typed[Double]() -> 3, new Typed[Int]() -> 5)
}

// Hit Ctrl-D

scala> bar.foo
res0: scala.collection.immutable.Iterable[Seq[Typed[Any]]] = List(Typed[Double], Typed[Double], Typed[Double], Typed[Int], Typed[Int], Typed[Int], Typed[Int], Typed[Int])
Run Code Online (Sandbox Code Playgroud)

请注意,我Typed在这个例子中创建了一个类以获得更好的输出.你当然可以坚持下去trait.

现在,为什么需要协方差?

协方差基本上意味着如果那样的A <: BX[A] <: X[B].所以,如果你被宣布testMapMap[Typed[Any], Int]同时Typed不变的,你不许例如通过Typed[Double]一个Typed[Any]即使Double <: Any.在这里,Scala编译器似乎取代_Any在协变的情况下(见即兴的评论关于此的详细阐述).

有关下划线问题的解释,我会参考Luigi的答案.