Недостающий метод Groovy не вызывается делегатом Closure для неявных вызовов не-GroovyObjects - PullRequest
2 голосов
/ 19 января 2020

Закрытие с делегатом, который не является groovy -объектом (например, происходит из обычной java -библиотеки), никогда не вызовет объект 'methodMissing', добавленный к этому делегату с использованием его метакласса, если вызов сделал 'неявным' (т.е. не вызывая его явно для 'делегата' в замыкании).

Приведенный ниже код делает явный и неявный вызов несуществующего метода; это делается для экземпляра класса Groovy, объекта GString и объекта, отличного от groovy. Единственный сбой - это неявный вызов не groovy -объекта (т.е. c. ArrayList).

(Вы можете увидеть и запустить тот же код онлайн: https://groovyconsole.appspot.com/edit/5200829376102400)

Не уверен, является ли это ошибкой или ограничением - ссылки на methodMissing, определенные через metaClass, довольно редки. Любые проницательные комментарии приветствуются.

class ClosureDelegate {
    def testMissingMethod(def someObject) {
        someObject.metaClass.methodMissing = { String name, args ->
            println name
        }
        def closure = {
            delegate.anything()
            anything() // this one fails on non-groovyclasses
        }
        closure.delegate = someObject
        closure.resolveStrategy = Closure.DELEGATE_ONLY
        closure()
    }
}

class TestObject {}

println "testing with TestObject"
new ClosureDelegate().testMissingMethod(new TestObject())
println "testing with GString"
new ClosureDelegate().testMissingMethod("${new Date()}")
println "testing with ArrayList"
new ClosureDelegate().testMissingMethod(new ArrayList())
testing with TestObject
anything
anything
testing with GString
anything
anything
testing with ArrayList
anything
Caught: groovy.lang.MissingMethodException: No signature of method: ClosureDelegate$_testMissingMethod_closure2.anything() is applicable for argument types: () values: []
Possible solutions: toString(), toString(), any(), any()

1 Ответ

1 голос
/ 24 января 2020

Согласно реализации ClosureMetaClass такое поведение ожидается. Взгляните на следующую часть, которая начинается со строки 275 в этом файле:

    switch (resolveStrategy) {
        case Closure.TO_SELF:
            break;
        case Closure.DELEGATE_ONLY:
            method = getDelegateMethod(closure, delegate, methodName, argClasses);
            callObject = delegate;
            if (method == null) {
                invokeOnDelegate = delegate != closure && (delegate instanceof GroovyObject);
            }
            break;

Источник: src / main / java / org / codehaus / groovy / runtime / metaclass / ClosureMetaClass .java # L275-L284

Логический флаг invokeOnDelegate проверяет, расширяет ли объект делегата GroovyObject (родительский класс по умолчанию для всех Groovy классов.) Когда вы выполняете ваш код с Groovy классами, этот флаг установлен на true и вызывается метод anything из объекта делегата. В случае классов, отличных от Groovy, выбрасывается MethodMissingException.

Вы можете отладить это поведение, установив точку останова в AbstractCallSite.callCurrent(GroovyObject receiver) в строке 160. Вы увидите это в во всех случаях метод anything не найден в замыкании, но в первых двух случаях invokeOnDelegate оценивается как true, и выполняется invokeMethod для объекта делегата. Это не происходит в третьем случае, потому что ArrayList не является экземпляром GroovyObject.

...