我在代码中广泛使用scala的2.10 TypeTags,我观察到一些与类型推断相关的行为,我不理解。
示例代码:
import scala.reflect.runtime.universe._
object BugDemo extends App {
def printType[T <: AnyRef : TypeTag]() : T = {
println(typeOf[T]);
null.asInstanceOf[T];
}
case class Foo()
var foo1 = printType[Foo]()
var foo2 : Foo = printType[Foo]()
var foo3 : Foo = printType()
}
现在我希望它打印 Foo
3 次,但实际上我得到 (scala 2.10.3):
BugDemo.Foo
BugDemo.Foo
Nothing
我的代码有什么问题?为什么 scala 不想在 foo3
中为我的T
传递正确的类型标签?
这里没有错。
未指定类型参数的情况下,scala 编译器采用最具体的类型。在printType()
的情况下,它是Nothing
(scala 类型层次结构中的底部类型)。
由于Nothing
是Foo
代码有效的子类型:
var foo3 : Foo = printType[Nothing]()
有趣的是,有了var foo3 : Nothing = printType[Nothing]()
甚至printType[Nothing]()
,你都会得到一个NullPointerException
:
scala> var foo3 : Nothing = printType[Nothing]()
Nothing
java.lang.NullPointerException
scala> printType[Nothing]()
Nothing
java.lang.NullPointerException
你无法获得Nothing
的实例,但是你的代码(var foo3 : Foo = printType[Nothing]()
)由于类型擦除而工作。它看起来像一个无法修复的 scala 错误。
当调用方未显式提供类型参数时,scala 推断的类型参数没有任何内容。我讨厌(讨厌)这种默认行为,因为它会导致各种运行时错误,没有什么是所有内容的允许参数。
最好通过要求类型参数不是 Nothing,在编译时完全指定类型参数:
sealed trait NotNothing[-T]
object NotNothing {
implicit object YoureSupposedToSupplyAType extends NotNothing[Nothing]
implicit object notNothing extends NotNothing[Any]
}
然后:
def printType[T : TypeTag : NotNothing]() : T = { ... }