4

我偶然发现了一种奇怪的情况,即在似乎更合适的地方推断出reflect.runtime.universe._进口原因。reflect.runtime.universe.RuntimeClassNothing

考虑这个简单的方法和List

import scala.reflect.ClassTag

def find[A : ClassTag](l: List[Any]): Option[A] =
    l collectFirst { case a: A => a }

val list = List(1, "a", false)

我可以使用它来查找某种类型的第一个元素List,并且效果很好,正如预期的那样。

scala> find[String](list)
res1: Option[String] = Some(a)

scala> find[Long](list)
res2: Option[Long] = None

如果我不提供类型参数,则A推断为Nothing,所以我得到Option[Nothing],也如预期的那样。

scala> find(list)
res3: Option[Nothing] = None

但是,如果我import scala.reflect.runtime.universe._再次不提供类型参数,A现在推断为reflect.runtime.universe.RuntimeClass而不是Nothing.

scala> find(list)
res4: Option[reflect.runtime.universe.RuntimeClass] = None
                ^ What?

这不是一个大问题,因为如果不find手动提供类型参数,我几乎无法想象该方法的很多用例,但为什么会发生这种情况呢?似乎部分归咎于,因为ClassTag再次删除它会导致Nothing推断(尽管由于擦除而完全破坏了该方法)。这里发生了什么?

4

2 回答 2

4

Universe这看起来像是内部设计运行时的一些完全意想不到的副作用。

scala.reflect.runtime.universe类型scala.reflect.api.JavaUniverse

通过导入其所有成员,您可以导入 - 特别是 - 一组ClassTag在 trait 中定义的隐式值,这些值scala.reflect.api.ImplicitTags由 Universe 扩展。

ImplicitTags特征引入了大约 90 种不同的隐含ClassTag值。其中,有这样一个:

implicit val RuntimeClassTag: ClassTag[RuntimeClass]

看起来编译器比其他人更喜欢它,因为它决定在推断任意ClassTag[A]. 这是为什么?这是因为RuntimeClassTag覆盖并且子类中scala.reflect.api.JavaUniverse定义的隐式值优先于超类中定义的隐式值(如重载决议规则中的某处所指定 - SLS 6.26.3)

因此,总而言之,编译器推断RuntimeClassA带有ClassTag上下文绑定,因为它在范围内看到了这个东西(由 Universe 上的通配符导入引入):

trait JavaUniverse extends Universe { self =>
  type RuntimeClass = java.lang.Class[_]
  implicit val RuntimeClassTag: ClassTag[RuntimeClass] = 
    ClassTag[RuntimeClass](classOf[RuntimeClass])
  ...
}
于 2015-05-10T08:41:59.780 回答
0

@ghik 的回答是正确的,但我认为有一件重要的事情要补充。通常,当我们需要隐式时ClassTag[A],我们希望编译器ClassTag为我们生成。我期望ClassTag[Nothing]在省略显式类型参数时生成。但是在范围内导入ClassTag[RuntimeClass]似乎可以阻止这种情况的发生。

我们可以通过简单地引入另一个隐式ClassTag而不导入来实现这一点runtime.universe._

scala> implicit val ct = classTag[Long]
ct: scala.reflect.ClassTag[Long] = Long

scala> find(list)
res8: Option[Long] = None

编译器看到我只有一个隐含ClassTag[Long]的范围,所以当我调用时find(list),编译器假定must 是我想要的并且Amust be Long,因为没有其他ClassTag可用的,它不会ClassTag为我生成另一个。

那么,在我的问题的确切上下文中,我正在导入大量隐式ClassTags,编译器认为这ClassTag[RuntimeClass]是其中最合适的。

于 2015-05-10T14:09:10.823 回答