Hello Groovy & Java-экспертыПерегрузка метода Groovy: выбор метода предпочитает интерфейсы по подклассам?
Мы столкнулись с необычным поведением 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 (после некоторого дополнительного смещения, как штраф за типы объектов Param), а затем сравнить с расстояния 2 для обоих оных методов с только интерфейсы в своих подписях, что приводит к тому, что метод #add (C c) не выбирается/не рассматривается. Исключение возникает из-за того, что в настоящее время существует 2 метода (#add (A a) и #add (B b)), которые имеют одинаковое расстояние, а среда выполнения не может знать, какой метод выбрать.
Возможно, кто-то может объяснить нам, почему в Groovy это обрабатывается по-разному по сравнению с Java?
Я также добавил этот вопрос в список рассылки заводной-Dev. Найдите его здесь: http://groovy.markmail.org/thread/4tya5t3huzhhfc6i – Christof