为什么重载具有不同上限的多态方法无法在Scala中编译

Fab*_*ner 7 scala overloading type-erasure

为什么以下不能在Scala中编译:

class A
class B

object X {
  def f[Q <: A](q: Q): Q = q
  def f[Q <: B](q: Q): Q = q
}
Run Code Online (Sandbox Code Playgroud)

有错误消息

<console>:16: error: method f is defined twice
  conflicting symbols both originated in file '<console>'
         def f[Q <: B](q: Q): Q = q
Run Code Online (Sandbox Code Playgroud)

据我所知,在类型擦除之后,def f[Q <: A](q: Q): Q应该用它的上限替换:def f(q: A): Any第二次f相应地重载.所以在类型擦除后它们应该是可区分的.

那么为什么Scala还在抱怨?

dk1*_*k14 5

只是为了补充@chengpohi的答案,您实际上可以使用类型类实现静态调度(重载是一种特殊情况):

trait A
trait B

implicit class RichA[Q <: A](q: Q){ def f = q }

implicit class RichB[Q <: B](q: Q){ def f = q }

scala> (new A{}).f
res0: A = $anon$1@39c1fe0b

scala> (new B{}).f
res1: B = $anon$1@20011bf
Run Code Online (Sandbox Code Playgroud)

它之所以不能自然运行,只是因为Scala必须模仿Java的重载(擦除)以保持代码与外部Java代码和Scala的内部特性和保证兼容.在你的情况下(但不总是)重载基本上是一个静态调用,所以它可以在编译时处理,但不幸的invokestatic是JVM 在运行时调度:

在执行方法调用之前,将解析标识的类和方法.有关如何解决方法的说明,请参见第9章.

invokestatic查看给定的描述符,并确定该方法采用的参数数量(这可能为零).它将这些参数从操作数堆栈中弹出.然后,它搜索由类定义的静态方法列表,使用描述符描述符定位方法methodname.

所以,无论它知道Q <: A限制 - 它都不知道Q运行时的正式类型,所以像@chengpohi指向的那些情况似乎无法检测或解决(实际上他们可以根据信息来做)从线性化 - 唯一的缺点是运行时类型涉及调度).


例如,Haskell在编译时确定了正确的方法(据我所知),因此类型类能够通过决定在编译时调用正确的方法来弥补缺乏真正的静态调度.

PS请注意,在Haskell中,重载用于动态调度(模式匹配)和静态类的类,因此与Java相比,它基本上是相反的.


mdm*_*mdm 3

重新发布评论作为答案以提高可见性。

我发现这篇旧文章似乎是同一个问题:http://www.scala-lang.org/old/node/4625.html

这似乎是 Scala 编译器的一个已知问题,必须做更多的工作,因为在不破坏编译器的其他(仅限 Scala)功能和保证的情况下很难支持此功能。该帖子还展示了一些解决方法。

如果这里的任何编译器专家能够阐明Dotty - 或者我应该说Skala吗?;) - 将计划修复它。

  • 仅当您命名的类型超过 20 个字符(最好是德语)时,Skala 才会支持它,因此编译器可以轻松区分经典和高级情况;) (3认同)