How can we replace a interface method in groovy?

Viewed 81

Question

How can I overwrite a method in Groovy if the class implements an interface? If the class does not implement an interface I can overwrite the method, but if the class implements an interface the method does not overwrite.

Example without implement

interface IA {
    void abc()
}

class A {
    void abc()  {
        println "original"
    }
}

x= new A()
x.abc()
x.metaClass.abc = {-> println "new" }
x.abc()

The output of this is

original
new

Example with implement

Consider the following example where class A implements interface IA

interface IA {
    void abc()
}

class A implements IA {
    void abc()  {
        println "original"
    }
}


x= new A()
x.abc()
x.metaClass.abc = {-> println "new" }
x.abc()

The output in this case is

original
original
2 Answers

As mentioned in the comments, looks like this is a pretty embarrassing bug, unfortunately.

But depending on your actual scenario, it may be possible to work around the problem.

Use Groovy @Category

If you can control where the method will be called, then you can use a Groovy Category to replace the method within a block:

x= new A()
x.abc()

@Category(A)
class ReplaceAbc {
    void abc() {
        println 'new'
    }
}

use(ReplaceAbc) {
    x.abc()
}

Replace the instance with an anonymous sub-type of the original type

If you can re-assign the variable, then this is an obvious way to override the method:

x= new A()
x.abc()
x = new A() {
    @Override
    void abc() {
        println 'new'
    }
}
x.abc()

How can I overwrite a method in Groovy if the class implements an interface?

There are a few ways to do it. One is with runtime extension methods.

See the project at github.com/jeffbrown/victorchoymetaprogramminginterface.

lib/src/main/groovy/victorchoymetaprogramminginterface/IA.groovy

package victorchoymetaprogramminginterface

interface IA {
    String abc()
}

lib/src/main/groovy/victorchoymetaprogramminginterface/A.groovy

package victorchoymetaprogramminginterface

class A implements IA{
    @Override
    String abc() {
        'A.abc'
    }
}

lib/src/main/groovy/victorchoymetaprogramminginterface/extensions/SomeInterfaceExtensionMethods.groovy

package victorchoymetaprogramminginterface.extensions

import victorchoymetaprogramminginterface.IA

class SomeInterfaceExtensionMethods {

    static String abc(IA ia) {
        'SomeInterfaceExtensionMethods.abc'
    }
}

lib/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule

moduleVersion=1.0
moduleName=Demo Extensions
staticExtensionClasses=victorchoymetaprogramminginterface.extensions.SomeInterfaceExtensionMethods

The following test passes:

lib/src/test/groovy/victorchoymetaprogramminginterface/TestInterfaceExtensions.groovy

package victorchoymetaprogramminginterface

import spock.lang.Specification

class TestInterfaceExtensions extends Specification {
    def "test interface extensions"() {
        setup:
        def obj = new A()

        expect:
        obj.abc() == 'SomeInterfaceExtensionMethods.abc'
    }
}
Related