为什么以下内容无法在Scala中编译:
class A
class B
object X {
def f[Q <: A](q: Q): Q = q
def f[Q <: B](q: Q): Q = q
}
带有错误消息
<console>:16: error: method f is defined twice
conflicting symbols both originated in file '<console>'
def f[Q <: B](q: Q): Q = q
根据我的理解,在类型擦除之后,def f[Q <: A](q: Q): Q
应该替换为其上限:def f(q: A): Any
和第二个重载f
。因此,在类型擦除后它们应该是可区分的。
那么为什么Scala还在抱怨?
答案 0 :(得分: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
它不能自然工作的原因只是Scala必须模仿Java的重载(擦除)以保持代码与外部Java代码和Scala的内部功能和保证兼容。在你的情况下重载(但不总是)基本上是一个静态调用,所以它可以在编译时处理,但JVM的invokestatic
不幸地调度in runtime:
在执行方法调用之前,将解析标识的类和方法。有关如何解决方法的说明,请参阅第9章。
invokestatic查看和中给出的描述符 确定该方法采用的参数数量(这可能为零)。它 从操作数堆栈中弹出这些参数。然后它搜索列表 由类定义的静态方法,定位方法methodname 带有描述符描述符。
所以,无论它知道Q <: A
限制 - 它都不知道运行时中Q
的正式类型,所以像@chengpohi指向的那些情况似乎不可能检测或解决(实际上他们可以根据线性化的信息来做 - 唯一的缺点是运行时类型涉及调度)。
P.S。请注意,在Haskell中,重载用于动态调度(模式匹配)和静态类的类,因此与Java相比,它基本上是相反的。
答案 1 :(得分:3)
重新发布评论作为提高可见度的答案。
我发现这篇旧文章似乎是同一个问题:http://www.scala-lang.org/old/node/4625.html
对于Scala编译器来说,它似乎是一个已知问题,必须做更多事情,因为在不破坏其他(仅限Scala)功能和保证的情况下很难支持此功能。编译器。该帖子也显示了一些解决方法。
如果SO上的任何编译器专家能够阐明Dotty是否 - 或者我应该说Skala,那将是非常有趣的? ;) - 计划修复它。
答案 2 :(得分:2)
这不仅 Scala 支持此功能, Java 也不支持此功能:
consumer.poll
它等于 Java :
def f[Q <: A](q: Q): Q = q
def f[Q <: B](q: Q): Q = q
众所周知,类型擦除将删除运行时中的类型,例如,类型public <Q extend A> Q f(q: Q) { return q;}
public <Q extend B> Q f(q: Q) { return q;}
是C
和{{1}的子类型在运行时,它会混淆哪个A
B
。
有一段代码片段可能对理解这一点很有帮助:
正如 Scala f
:
apply
所以trait
是trait A
trait B
class C extends A with B
和C
的子类。如果将其传递给A
方法,则会导致运行时中的混淆。
所以在 Java 中,我们可以使用B
来声明子类。这也会导致运行时中的混淆。例如:
f
答案 3 :(得分:2)
我想指出上述内容在Java中是可行的。它的行为符合white-space: nowrap;
和a
的预期。由于多个mixin,我可以在Scala中看到traits的问题,但是不可能有多个类的继承。
b