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
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'
}
}
I try to understand more about how Geb and Spock work internally to understand what is really happening in my tests.
I found that GebSpec which I extend to write my tests has a field Browser _browser.
I also found that GebSpec has a method getBrowser() which returns _browser, so _browser can be accessed over getBrowser() and get_browser(). But the interesting part is that while debugging in intelliJ expanding an instance of GebSpec shows no field _browser but only a field $spock_sharedField__browser.
A little example:
Debugging my Class: The instance of GebSpec has a field spock_sharedField__browser but no field _browser
How do they manage to hide the _browser field from me in the debugger and why do they do it?
Recall a field Browser _browser is declared in GebSpec and a field $spock_sharedField__browser is not.
There also is no method get$spock_sharedField__browser() but I still can access and manipulate $spock_sharedField__browser.
I tried to do it myself:
I wrote a class TestClass that declares _browser exactly analog to GebSpec, but if I debug here the field _browser is shown normally as one would expect
Can someone explain me what is going on?
Why hide _browser?
What is $spock_sharedField__browser good for?
UPDATE: I think the following code describes summarizes it pretty good:
import geb.spock.GebSpec
class GebHomeSpec extends GebSpec{
def "test Geb homepage"(){
when:
['get$spock_sharedField__browser', 'getBrowser', 'get_browser'].each {
try {
println this."${it}"()
} catch (MissingFieldException e) {
println e
}
}
['$spock_sharedField__browser', 'browser', '_browser'].each {
try {
println this.getMetaClass().getAttribute(this, it)
} catch (MissingFieldException e){
println e
}
}
then:
true
}
}
The result on the console is:
null
geb.Browser#352ff4da
geb.Browser#352ff4da
null
groovy.lang.MissingFieldException: No such field: browser for class: GebHomeSpec
groovy.lang.MissingFieldException: No such field: _browser for class: GebHomeSpec
My interpretation, considering the answer of kriegaex, is that in the Compilation during the Spock transformation the field $spock_sharedField__browser is declared and the field _browser is removed. The field browser never existed. But there are still getters for browser and _browser. I wonder where there get their data from (in this case geb.Browser#352ff4da) as none of the field exists anymore as the exceptions show. At least it matches with the debugging information (c.f. first picture/link) that shows the field $spock_sharedField__browser but neither a field _browser nor a field browser.
Finally I noticed (and I dont really know how to explain that) the getters for _browser and browser are outside of the class no longer available (see below). I thought the concept of private is not implemented in groovy and making getters private makes no sense to me anyways.
import geb.spock.GebSpec
class Main {
static void main(String[] args) {
GebSpec gebSpec = new GebSpec()
['get$spock_sharedField__browser', 'getBrowser', 'get_browser'].each {
try {
println gebSpec."${it}"()
} catch (MissingFieldException e) {
println e
}
}
['$spock_sharedField__browser', 'browser', '_browser'].each {
try {
println gebSpec.getMetaClass().getAttribute(gebSpec, it)
} catch (MissingFieldException e){
println e
}
}
}
}
This leads to
null
groovy.lang.MissingFieldException: No such field: $spock_sharedField__browser for class: org.codehaus.groovy.runtime.NullObject
groovy.lang.MissingFieldException: No such field: $spock_sharedField__browser for class: org.codehaus.groovy.runtime.NullObject
null
groovy.lang.MissingFieldException: No such field: browser for class: geb.spock.GebSpec
groovy.lang.MissingFieldException: No such field: _browser for class: geb.spock.GebSpec
All in all I find this rather confusing and I wonder what this is good for. Why introduce $spock_sharedField__browser and remove _browser?
If you use IntelliJ IDEA, you can just decompile the GebSpec class and will see something like this (this is what the Groovy compiler really produced when it compiled the library class):
public class GebSpec extends Specification implements GroovyObject {
// ...
#Shared
#FieldMetadata(
line = 29,
name = "_browser",
ordinal = 2
)
protected volatile Browser $spock_sharedField__browser;
// ...
public Browser createBrowser() {
CallSite[] var1 = $getCallSiteArray();
return !__$stMC && !BytecodeInterface8.disabledStandardMetaClass() ? (Browser)ScriptBytecodeAdapter.castToType(var1[8].callConstructor(Browser.class, this.createConf()), Browser.class) : (Browser)ScriptBytecodeAdapter.castToType(var1[6].callConstructor(Browser.class, var1[7].callCurrent(this)), Browser.class);
}
public Browser getBrowser() {
CallSite[] var1 = $getCallSiteArray();
if (BytecodeInterface8.isOrigZ() && !__$stMC && !BytecodeInterface8.disabledStandardMetaClass()) {
if (ScriptBytecodeAdapter.compareEqual(var1[11].callGroovyObjectGetProperty(this), (Object)null)) {
Browser var3 = this.createBrowser();
ScriptBytecodeAdapter.setGroovyObjectProperty(var3, GebSpec.class, this, (String)"_browser");
}
} else if (ScriptBytecodeAdapter.compareEqual(var1[9].callGroovyObjectGetProperty(this), (Object)null)) {
Object var2 = var1[10].callCurrent(this);
ScriptBytecodeAdapter.setGroovyObjectProperty((Browser)ScriptBytecodeAdapter.castToType(var2, Browser.class), GebSpec.class, this, (String)"_browser");
}
return (Browser)ScriptBytecodeAdapter.castToType(var1[12].callGroovyObjectGetProperty(this), Browser.class);
}
// ...
public Browser get$spock_sharedField__browser() {
return this.$spock_sharedField__browser;
}
public void set$spock_sharedField__browser(Browser var1) {
this.$spock_sharedField__browser = var1;
}
}
I think you have dived deep enough already to understand without further explanation.
Update: I forgot to mention: Your test class does not inherit GebSpec (which again inherits from Specification, i.e. the code will not be transformed by Spock/Geb because it has the wrong base class. If you do this, though:
package de.scrum_master.stackoverflow
import geb.spock.GebSpec
import spock.lang.Shared
class FooIT extends GebSpec {
#Shared
def myField
def test() {
expect:
true
}
}
Then the decompiled code will be:
package de.scrum_master.stackoverflow;
import geb.spock.GebSpec;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.spockframework.runtime.ErrorCollector;
import org.spockframework.runtime.SpockRuntime;
import org.spockframework.runtime.ValueRecorder;
import org.spockframework.runtime.model.BlockKind;
import org.spockframework.runtime.model.BlockMetadata;
import org.spockframework.runtime.model.FeatureMetadata;
import org.spockframework.runtime.model.FieldMetadata;
import org.spockframework.runtime.model.SpecMetadata;
import spock.lang.Shared;
#SpecMetadata(
filename = "FooIT.groovy",
line = 6
)
public class FooIT extends GebSpec {
#Shared
#FieldMetadata(
line = 7,
name = "myField",
ordinal = 0
)
protected volatile Object $spock_sharedField_myField;
public FooIT() {
CallSite[] var1 = $getCallSiteArray();
}
#FeatureMetadata(
line = 10,
name = "test",
ordinal = 0,
blocks = {#BlockMetadata(
kind = BlockKind.EXPECT,
texts = {}
)},
parameterNames = {}
)
public void $spock_feature_1_0() {
CallSite[] var1 = $getCallSiteArray();
ErrorCollector $spock_errorCollector = (ErrorCollector)ScriptBytecodeAdapter.castToType(var1[2].callConstructor(ErrorCollector.class, false), ErrorCollector.class);
ValueRecorder $spock_valueRecorder = (ValueRecorder)ScriptBytecodeAdapter.castToType(var1[3].callConstructor(ValueRecorder.class), ValueRecorder.class);
Object var10000;
try {
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "true", Integer.valueOf(12), Integer.valueOf(5), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), true));
var10000 = null;
} catch (Throwable var13) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "true", Integer.valueOf(12), Integer.valueOf(5), (Object)null, var13);
var10000 = null;
} finally {
;
}
var1[4].call(var1[5].call(this.getSpecificationContext()));
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
public Object get$spock_sharedField_myField() {
return this.$spock_sharedField_myField;
}
public void set$spock_sharedField_myField(Object var1) {
this.$spock_sharedField_myField = var1;
}
}
Update 2:
As for your additional questions, I can only speculate about the answers, I am sure users like #erdi (Geb maintainer), #Szymon Stepniak, #Leonard Brünings (who seem to be Groovy cracks, which I am not) could say more about it, but OTOH this is not a discussion forum and the questions are not particularly well suited for SO. Anyway, I edited the question tags to include "groovy" so as to maybe raise their attention.
Why introduce $spock_sharedField__browser and remove _browser?
I think it is just the result of Spock's way of transforming the #Shared annotation into a member variable with named so as to very unlikely collide with any existing member names. You also see this happening in the decompiled version of my own Spock/Geb specification.
But there are still getters for browser and _browser.
Of course there is a getter for browser, as in the Geb DLS you usually don't look behind the scenes but just use the syntactic sugar browser to access the browser instance. This Groovy-ism will call getBrowser(), as you probably know. This particular getter is declared explicitly in the GebSpec class in order to make the member conveniently accessible (you also see some lazy browser instantiation logic here):
Browser getBrowser() {
if (_browser == null) {
_browser = createBrowser()
}
_browser
}
I wonder where there get their data from (in this case geb.Browser#352ff4da) as none of the field exists anymore as the exceptions show.
I do not know enough about Groovy's dynamic language features to answer that, but you can see the actual mechanics in my decompiled code snippets.
Accessing Spock-specific class members from outside a running specification obviously does not work and probably is not meant to be. But if you run this test, it works just fine:
package de.scrum_master.stackoverflow
import geb.spock.GebSpec
import spock.lang.Shared
class FooIT extends GebSpec {
#Shared
def myField = "foo"
def test() {
given:
println browser
println myField
expect:
true
}
}
Console log:
geb.Browser#1722011b
foo
I want to attach public method to the class.
This is called extension method in C#.
package extensionMethods
class A {
def testA() {}
//def testB() {} Need to add a public method to this class A but we don't have access to the class
}
class B {
def test() {
def a = new A();
a.testA()
a.testB() //Need to add a public method to the Class A without defining the method in the class A
}
}
//In C# way -> Extension method
class C {
/* void testB(this A a) {
}*/
}
How can we achieve the similar approach in Groovy?
In the above example I want to attach method testB() to class A
You will want something like this:
package something
class SomeExtensionClass {
static void testB(A self) {
// ...
}
}
Then your META-INF/services/org.codehaus.groovy.runtime.ExtensionModule extension descriptor...
moduleName=Test module for specifications
moduleVersion=1.0-test
extensionClasses=something.SomeExtensionClass
See http://groovy-lang.org/metaprogramming.html#_extension_modules for more info.
I'm going to internationalize groovy API abit.
For final class (e.g. String)
String.metaClass.вСтроку = {-> this.toString() }
However, this will create additional closure. Isn't there any way to just alias method with another method?
Something like this:
String.metaClass.вСтроку = String.metaClass.&toString
You could use #Category transform like this
#Category(String) class StringInternationalization {
String вСтроку() {
this.toString()
}
int длина() {
this.length()
}
}
class ApplyMixin {
static {
String.mixin(StringInternationalization)
final helloString = "Привет мир!"
println helloString.вСтроку()
assert helloString.длина() == helloString.length()
}
}
new Main()
This will create 1 Category class for each localised class and one class to apply all mixin transformations(to register all methods.) Also should be faster, then individual closures.
More reading here: http://groovy.codehaus.org/Category+and+Mixin+transformations
I have a set of groovy scripts in package hierarchy. I have 1 main script, from which I want to call others. For example I have these scripts (with public classes/interfaces of the same name in them):
package.MainScript
package.MyInterface;
package.utils.MyInterfaceImpl1 //implements MyInterface
package.utils.MyInterfaceImpl2 //implements MyInterface
Is there a way to call one script from the other without knowing called class name at compile time? I mean to do something like dynamic class loading like:
class MainScript {
public static void main (String[] args) {
MyInterface instance = Class.forName("package.utils.Util1");
}
}
Yeah! Groovy is a dynamic language. You can create class instance dynamically.
package.MyInterface
class MyInterfaceImpl1 {
def greet() {
"Hello"
}
}
package.MyInterface
class MyInterfaceImpl2 {
def greet() {
"Hi!"
}
}
def name = 'MyInterfaceImpl1' // Choose whatever you want at runtime
def className = Class.forName("MyInterface.$name")
def instance = className.newInstance()
assert instance.greet() == 'Hello'