【发布时间】:2015-06-11 00:46:31
【问题描述】:
你好 Groovy 和 Java 专家
我们遇到了一种奇特的 Groovy 行为,在我们看来这就像语言中的一个限制(或错误)。我们的长篇文章归结为这个问题:
Groovy 中的方法选择是否有意选择接口而不是接口 当方法重载起作用时子类化?
我们创建了一个简单的例子来说明这个案例:
interface A {}
interface B {}
class C implements A, B {}
class D extends C {}
class Foo {
void add(A a) { System.out.println("A"); }
void add(B b) { System.out.println("B"); }
void add(C c) { System.out.println("C"); }
}
D d = new D();
new Foo().add(d);
我们预料到的是方法 Foo#add(C c) 正在被调用,但是,抛出了以下异常:
groovy.lang.GroovyRuntimeException: Ambiguous method overloading for method Foo#add.
Cannot resolve which method to invoke for [class D] due to overlapping prototypes between: [interface A] {interface B]
这似乎出乎意料,因为Foo#add(C c) 显然是最佳人选。因此,我们在 Java 中测试了这段代码,它按预期工作:方法 Foo#add(C c) 被调用。
然后我们继续进一步调查并通过源代码进行调试。具体来说,有一个方法可以选择调用哪些方法:groovy.lang.MetaClassImpl#chooseMostSpecificParams
在那里,计算所有 3 个(在我们的例子中)#add 方法之间的距离 - 最后 - 这里:org.codehaus.groovy.runtime.MetaClassHelper#calculateParameterDistance(java.lang.Class, org.codehaus.groovy.reflection.CachedClass)
然后算法依次增加距离。首先,因为在我们的例子中,参数d(D 的实例)不是接口,也不是原始类型,所以添加了 17 的距离。
其次,也只有这样,才检查类型 C 和 D 是否相同或 D 是否继承自 C。对于 C 高于 D 的每个继承级别,添加距离 3。因此,我们最终得到了 20 的距离。
这个 20 的距离(经过一些额外的移动,比如对对象参数类型的惩罚)然后与签名中带有 only 接口的两个添加方法的距离 2 进行比较,这导致方法#add(C c) 未被选择/考虑。发生异常是因为,现在确实有 2 个方法(#add(A a) 和 #add(B b))具有相同的距离,并且运行时无法知道选择哪个方法。
也许有人可以向我们解释为什么 Groovy 与 Java 的处理方式不同?
【问题讨论】:
-
我也将此问题添加到了 groovy-dev 邮件列表中。在这里找到它:groovy.markmail.org/thread/4tya5t3huzhhfc6i
标签: java groovy overloading