【问题标题】:Structural Type doesn't call correct implementation?结构类型不调用正确的实现?
【发布时间】:2016-02-16 15:37:33
【问题描述】:

我的印象是Structural Types 在后台使用反射(表明需要tell the compiler 来启用"-language:reflectiveCalls"),并且任何与该类型匹配的对象都将使用它自己的函数版本。例如,如果我在Seq 上调用.contains,那么它将使用Seq 版本,如果我在String 上调用它,那么它将使用从@987654330 获得的StringOps 中定义的版本@

那么在 scala 2.10.3 中,为什么会发生这种情况:

Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_79).
Type in expressions to have them evaluated.
Type :help for more information.

scala> type Containable = { def contains(elem:Any):Boolean }
defined type alias Containable

scala> val myMap: Map[String, Containable] = Map("A" -> "B", "C" -> Seq("A","B"))
myMap: Map[String,Containable] = Map(A -> B, C -> List(A, B))

scala> myMap("A").contains("B")
res0: Boolean = false

scala> myMap("C").contains("B")
res1: Boolean = true

scala> "B".contains("B")
res3: Boolean = true

如您所见,String.contains(String) 为自己返回 true,但如果它在被解释为 Containable 类型时被调用,则不会返回 true,即使它与 StringOps 类中定义的方法匹配.

我觉得这与== 的实现有关,因为.contains 文档说:

如果此序列有一个元素等于(由 == 确定)elem,则为 true,否则为 false。

通过isInstanceOf检查类型的结果加剧了这种感觉

scala> val aVal = myMap("A")
aVal: Containable = B

scala> aVal.isInstanceOf[String]
res5: Boolean = false

scala> aVal.isInstanceOf[Seq[_]]
res6: Boolean = true

针对关于编译器错误的评论,这里是screencast of my terminal showing this working

【问题讨论】:

  • 你确定你试过这样吗?在 Scala 2.11 中,我在实例化地图的行中得到类型不匹配。这是因为 String 和 Seq 都没有方法 contains(Any)。它们的包含方法具有不同的签名。因此,如果在 scala 2.10 中进行这种类型检查,这将是一个错误。
  • 这在 scala 2.10.3 中运行,版本在 REPL 中注明
  • @dth 我为你添加了我的 REPL 的视频截屏

标签: scala reflection structural-typing


【解决方案1】:

当您将Strings 插入您的Map 时,它们会被转换为WrappedString,因为String 没有您在Containable 中定义的签名的方法。

scala> implicitly[String => Containable]
res10: String => Containable = <function1>

scala> res10("s")
res11: Containable = s

scala> res11.getClass
res12: Class[_ <: AnyRef] = class scala.collection.immutable.WrappedString

在 Scala 2.10.x 中,WrappedString 有一个方法 contains(elem: Any): Boolean。它检查elem 是否是调用contains 的集合的元素。 WrappedString 表示 Chars 的集合,因此如果您给它一个 String,该方法将永远不会返回 true。 在 scala 2.11.x 中,contains 方法已更改,因此它只接受 Chars。

String 本身有一个方法contains(elem: java.lang.CharSequence): BooleanStringCharSequence,因此当您在 String 上调用 contains("B") 时,将调用该方法,并且 String 不会转换为 WrappedString

【讨论】:

  • 哇哦。你知道为什么它会被转换成 WrappedString 吗?
  • 我刚刚注意到,这种变化更深。 WrappedString 实现了 SeqLike,并且 SeqLike 的 contains 方法的签名在 Scala 2.11 中发生了变化。
  • 转换的原因是String没有方法contains(Any)而只有contains(CharSequence)。正如您明确指定的类型 Containable 编译器必须对其进行转换。
  • 对不起,我应该更具体一些,我知道它被转换是因为它没有 contains 方法,但我的问题更多的是为什么它变成 WrappedString 而不是 @ 987654346@,我假设它是 Map 中的实现细节?
  • 啊,没关系,StringOps.toCollection 是通过 WrappedString 转换它的,所以转换为使用 .contains 的集合特征可能就是这样,而且似乎没有办法绕过它。因此,如果我想在映射中存储字符串和字符串列表,我将不得不对结果进行模式匹配,而不是使用 Containable 键入它。太糟糕了,打字看起来很干净。
猜你喜欢
  • 2015-05-14
  • 2014-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多