Scala 泛型类型推断与 ClassTag 不一致

标签 scala generics reflection

我使用的是 Scala 2.10,发现在对泛型参数使用类型推断时出现了我认为很奇怪的行为。考虑下面的例子(注意 i 只是一个虚拟变量):

scala> import scala.reflect.{ClassTag,classTag}
     |
     | def broken[T : ClassTag](i : Int) : List[T] = {
     |   println("ClassTag is : " + classTag[T])
     |   List.empty[T]
     | }

import scala.reflect.{ClassTag, classTag}

broken: [T](i: Int)(implicit evidence$1: scala.reflect.ClassTag[T])List[T]

scala> broken(3)
ClassTag is : Nothing
res0: List[Nothing] = List()

scala> val brokenVal : List[String] = broken(3)
ClassTag is : Nothing
brokenVal: List[String] = List()

调用 broken(3) 似乎是一致的,因为函数内的打印语句与推断的 ClassTag 一致。

但是,第二个语句,编译器为 ClassTag 推断并在函数内部打印的内容与实际返回类型之间存在不一致。

我希望编译器从类型声明中推断出 ClassTag,即 List[String]。这不对吗?谁能赐教一下?

这里有一些进一步的上下文:我实际上在一些代码中使用类标签来过滤集合(此处未显示),当我没有明确指定 T 时,它显然会失败,因为它是与 Nothing 类型进行比较。

最佳答案

第二个示例的实际返回 类型是List[Nothing],因此ClassTag 与它完全一致。发生这种情况是因为 List 是协变的(定义为 List[+A] 而不仅仅是 List[A])。这种协方差允许您编写类似 list = Nil 的语句(因为 Nil.type = List[Nothing]),但它也允许编译器将底部类型推断为列表类型参数,因为 List[Nothing]List[String] 的子类型,同样,由于它的协方差。所以实际的类型不匹配是在返回类型和 brokenVal 的类型之间。

为了克服这个限制,你可以使用不变类型作为你的返回类型,即:

import scala.reflect.ClassTag

class Invariant[T]

def f[T: ClassTag](i: Int): Invariant[T] = {
  println("ClassTag: " + implicitly[ClassTag[T]])
  new Invariant[T]
}

val ret: Invariant[String] = f(3)
// ClassTag: java.lang.String
// ret: Invariant[String] = Invariant@30af5b6b

另一方面,您可以让编译器从其他内容推断T,例如,通过传递类型T 的值作为参数。你不能让这个例子在普通 List 上工作,因为你不能改变它们的方差。

关于Scala 泛型类型推断与 ClassTag 不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36456198/

相关文章:

java - 为什么这些函数删除后类型相同?

Java 泛型 - 类型不重要

java - 如何从实现接口(interface)的泛型类返回接口(interface)?

java - 类.getConstantPool()

c# - 自定义属性对抽象属性的继承

PHP 枚举层次结构

scala - 相同类型的 Apache Spark 类型不匹配(字符串)

scala - Seq[AnyVal] 作为类型而不是 Seq[Int] 返回

scala - 在 Scala 中使用 def 定义字段是什么意思?

scala - 在 Scala 中删除 BigDecimal 的尾随零的正确方法是什么?