我偶然发现了一个奇怪的情况: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>
被推断(尽管由于擦除而完全破坏了方法)。这里发生了什么?
答案 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]
时使用它。这是为什么?这是因为RuntimeClassTag
在scala.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>