当reflect.runtime.universe._存在时,为什么reflect.runtime.universe.RuntimeClass推断为Nothing?

时间:2015-05-10 04:55:36

标签: scala scala-reflect

我偶然发现了一个奇怪的情况:reflect.runtime.universe._导入reflect.runtime.universe.RuntimeClass导致Nothing被推断出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似乎有部分责任,因为再次删除它会导致<xp:repeat id="repeat1" rows="30" var="playerData"> <xp:this.value><![CDATA[#{javascript: sessionAsSigner.getDatabase(database.getServer(), "PrivDb.nsf").getView("PrivView").getAllEntries(); }]]></xp:this.value> ... </xp:repeat> 被推断(尽管由于擦除而完全破坏了方法)。这里发生了什么?

2 个答案:

答案 0 :(得分:4)

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

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

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

ImplicitTags特征引入了大约90个不同的隐式ClassTag值。其中有一个:

implicit val RuntimeClassTag: ClassTag[RuntimeClass]

看起来编译器比其他人更喜欢它,因为它决定在推断任意ClassTag[A]时使用它。这是为什么?这是因为RuntimeClassTagscala.reflect.api.JavaUniverse中被重写,并且子类中定义的隐式值优先于超类中定义的隐式值(在重载解析规则中的某处指定 - SLS 6.26.3 )

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

trait JavaUniverse extends Universe { self =>
  type RuntimeClass = java.lang.Class[_]
  implicit val RuntimeClassTag: ClassTag[RuntimeClass] = 
    ClassTag[RuntimeClass](classOf[RuntimeClass])
  ...
}

答案 1 :(得分: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)时,编译器假设必须是我想要的和{{1 }}必须是A,因为没有其他Long可用,并且它不会为我生成另一个ClassTag

在我的问题的确切背景下,我导入了一个隐含ClassTag的船载,编译器认为ClassTag是最合适的。{/ p>