如何return通配符泛型?

How to return wildcard generic?

我有一个带有参数的类型别名,我想 return 来自一个方法的不同参数类型的实例:

type TC[T] = (ClassTag[T], Option[T])

def gen(x: Int): TC[_] = x match {
  case 0 => (classTag[Int], Option[Int](0))
  case _ => (classTag[String], Option[String](""))
}

这不起作用并给我错误:

error: type mismatch; found : (scala.reflect.ClassTag[_ >: Int with String], Option[Any]) required: TC[] (which expands to) (scala.reflect.ClassTag[], Option[_]) forSome { type _ }

我试过用Any代替通配符_,还是不行:

def gen(x: Int): TC[Any]

On line 2: error: type mismatch; found : scala.reflect.ClassTag[Int] required: scala.reflect.ClassTag[Any] Note: Int <: Any, but trait ClassTag is invariant in type T. You may wish to investigate a wildcard type such as _ <: Any. (SLS 3.2.10) case _ => (classTag[String], Some("")) ^ On line 3: error: type mismatch; found : scala.reflect.ClassTag[String] required: scala.reflect.ClassTag[Any] Note: String <: Any, but trait ClassTag is invariant in type T. You may wish to investigate a wildcard type such as _ <: Any. (SLS 3.2.10)

如何实现?

return 特定类型比存在类型更好。如果你想要 gen 到 return 不同的类型取决于它的参数那么实际上 gen 是一个多态函数。尝试使用类型 class 和单例类型的以下方法。

type TC[T] = (ClassTag[T], Option[T])

trait Gen[X <: Int] {
  type Out
  def apply(x: X): Out
}
trait LowPriorityGen {
  type Aux[X <: Int, Out0] = Gen[X] { type Out = Out0 }
  def instance[X <: Int, Out0](f: X => Out0): Aux[X, Out0] = new Gen[X] {
    override type Out = Out0
    override def apply(x: X): Out0 = f(x)
  }

  implicit def default[X <: Int : ValueOf]: Aux[X, TC[String]] = instance(_ => (classTag[String], Option[String]("")))
}
object Gen extends LowPriorityGen {
  implicit val zero: Aux[0, TC[Int]] = instance(_ => (classTag[Int], Option[Int](0)))
}

def gen[X <: Int with Singleton](x: X)(implicit g: Gen[X]): g.Out = g(x)

gen(0) //(Int,Some(0))
gen(1) //(java.lang.String,Some())

原因与中的相似。 ClassTagOption 有不同的方差。

尝试

type TC[T] = (ClassTag[_ <: T], Option[T])

def gen(x: Int): TC[_] = x match {
  case 0 => (classTag[Int], Option[Int](0))
  case _ => (classTag[String], Option[String](""))
}

即使您无法在类型中编码所需的 属性,您仍然可以在编译时使用模式匹配右侧的 check 检查它。

def gen(x: Int): (ClassTag[_], Option[_]) = x match {
  case 0 => check(classTag[Int], Option[Int](0))
  case _ => check(classTag[String], Option[String](""))
}

def check[T](classTag: ClassTag[T], option: Option[T]): (ClassTag[T], Option[T]) = (classTag, option)

事实证明,因为 Tuple4 类型参数是协变的:Tuple4[+T1, +T2, +T3, +T4],它不能很好地与不变类型 class 一起使用,例如 ClassTag.

我创建了一个采用不变类型参数的包装器 class:

case class TC[T](ct: ClassTag[T], o: Option[T])

def gen(x: Int): TC[_] = x match {
  case 0 => TC(classTag[Int], Option[Int](0))
  case _ => TC(classTag[String], Option[String](""))
}

瞧,它起作用了。