2014-09-25 4 views
4

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?

+0

Я также добавил этот вопрос в список рассылки заводной-Dev. Найдите его здесь: http://groovy.markmail.org/thread/4tya5t3huzhhfc6i – Christof

ответ

2

Это звучит как более конкретный случай this (unsolved) bug. Я бы предложил заполнить JIRA об этом.


Выбор метода Groovy работает во время выполнения с multiple dispatch, or multimethods, из-за того, чтобы быть динамический язык с возможностью набора текста, в то время как Java использует единую отправку, где метод, который будет вызываться определяется во время компиляции.

Следующий код работает на Java, но терпит неудачу с ошибкой утверждение в Groovy:

public class SingleMult { 
    public static void main(String[] args) { 
    A a = new B(); 
    assert(new SingleMult().method(a) == "A"); 
    } 

    String method(A a) { return "A"; } 
    String method(B b) { return "B"; } 
} 

interface A {} 

class B implements A {} 
+0

Правильно, спасибо за ваш пример. Связанная ошибка связана, хотя, на наш взгляд, не совсем то же самое: в ней они обсуждают только проблему, когда класс реализует несколько интерфейсов. Тем не менее, в нашем случае мы считаем, что это ошибка, потому что ясно, что D ближе к C, чем любой из методов, типизированных для интерфейса. – Christof

+0

Я по-прежнему принимаю ваш ответ;) – Christof

+0

У меня есть аналогичная проблема: класс с 3 методами, один принимает Runnable, один Callable и последний Closure. При вызове метода с закрытием используется метод Runnable. Это та же проблема? –

Смежные вопросы