groovy metaclass replace superclass method - groovy

Is there a way to replace using the metaclass object, a method that is of a super class.
Example:
class A {
def doIt(){
two()
println 'do it!'
}
protected two(){
println 'two'
}
}
class B extends A{
def doLast(){
doIt()
}
}
B b = new B();
b.doIt()
/*
* two
* doit!
*/
b.metaClass.two = {
println 'my new two!'
}
b.doIt();
/*
* my new two!
* do it!
*/

Since two and doIt are declared in the same class, groovy will skip the meta-object protocol for this call. You can override this behavior by marking the super class as GroovyInterceptable, which forces all method calls to go through invokeMethod. For example:
class A implements GroovyInterceptable {
def doIt(){
two()
println 'do it!'
}
protected two(){
println 'two'
}
}
class B extends A {
def doLast(){
doIt()
}
}
B b = new B()
b.doIt() // prints two
b.metaClass.two = {
println 'my new two!'
}
b.doIt() // prints my new two!

Related

How to override Groovy variable and method using anonymous class?

I have the following code. I have an abstract JobParams, a class extending that abstract GradleJobParams, and a gjp variable with value using anonymous class declaration.
I want to test the overriding behavior of groovy. I can override the method setupRoot() but not the property testVar, why is that?
Tested on: https://groovyconsole.appspot.com/script/5146436232544256
abstract class JobParams {
int root
def testVar=1
def setupRoot () {
println("The root");
}
def printTestVar () {
println("The testVar:" + testVar);
}
}
class GradleJobParams extends JobParams {
}
def gjp = [
testVar:3,
setupRoot:{
println("Override root");
}
] as GradleJobParams;
println("Starting");
gjp.printTestVar();
gjp.setupRoot();
The result is:
Starting
The testVar:1
Override root
Java (and thus Groovy) does not support overriding fields from the parent class with subclassing. Instead, it uses a mechanism called hiding fields:
Hiding Fields
Within a class, a field that has the same name as a field in the superclass hides the superclass's field, even if their types are different. Within the subclass, the field in the superclass cannot be referenced by its simple name. Instead, the field must be accessed through super, which is covered in the next section. Generally speaking, we don't recommend hiding fields as it makes code difficult to read.
Source: https://docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html
It can be simply illustrated with the following example in Java:
final class SubclassHiddingFieldExample {
static abstract class A {
int value = 10;
void printValue1() {
System.out.println(value);
}
void printValue2() {
System.out.println(this.value);
}
void printValue3() {
System.out.println(((B)this).value);
}
}
static class B extends A {
int value = 12;
}
public static void main(String[] args) {
final B b = new B();
b.printValue1();
b.printValue2();
b.printValue3();
}
}
Output:
10
10
12
As you can see, only printValue3 prints out 3, because it cast this explicitly to B class.
Now, if you look at the decompiled bytecode of your JobParams class, you can see that the printTestVar method code is an equivalent of the following Java code:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.callsite.CallSite;
public abstract class JobParams implements GroovyObject {
private int root;
private Object testVar;
public JobParams() {
CallSite[] var1 = $getCallSiteArray();
byte var2 = 1;
this.testVar = Integer.valueOf(var2);
MetaClass var3 = this.$getStaticMetaClass();
this.metaClass = var3;
}
public Object setupRoot() {
CallSite[] var1 = $getCallSiteArray();
return var1[0].callCurrent(this, "The root");
}
public Object printTestVar() {
CallSite[] var1 = $getCallSiteArray();
return var1[1].callCurrent(this, var1[2].call("The testVar:", this.testVar));
}
public MetaClass getMetaClass() {
MetaClass var10000 = this.metaClass;
if (var10000 != null) {
return var10000;
} else {
this.metaClass = this.$getStaticMetaClass();
return this.metaClass;
}
}
public void setMetaClass(MetaClass var1) {
this.metaClass = var1;
}
public Object invokeMethod(String var1, Object var2) {
return this.getMetaClass().invokeMethod(this, var1, var2);
}
public Object getProperty(String var1) {
return this.getMetaClass().getProperty(this, var1);
}
public void setProperty(String var1, Object var2) {
this.getMetaClass().setProperty(this, var1, var2);
}
public int getRoot() {
return this.root;
}
public void setRoot(int var1) {
this.root = var1;
}
public Object getTestVar() {
return this.testVar;
}
public void setTestVar(Object var1) {
this.testVar = var1;
}
}
You can see that the line that prints out the value of the testVar field is represented by:
return var1[1].callCurrent(this, var1[2].call("The testVar:", this.testVar));
It means that no matter what value of testVar your subclass defines, the printTestVar method uses testVar field defined in the JobParams class. Period.
Using Groovy auto getter methods
There is one way you to implement the expected behavior. Every class field in Groovy has a getter method associated with that field compiled by Groovy for you. It means that you can access testVar by calling the getTestVar() method generated by the Groovy compiler. You can use it to override the value returned by a getter method for any field from the subclass. Consider the following example:
abstract class JobParams {
int root
def testVar=1
def setupRoot () {
println("The root");
}
def printTestVar () {
println("The testVar:" + getTestVar()); // <-- using a getTestVar() method instead a testVar field
}
}
class GradleJobParams extends JobParams {
}
def gjp = [
getTestVar: 3, // <-- stubbing getTestVar() method to return a different value
setupRoot:{
println("Override root");
}
] as GradleJobParams;
println("Starting");
gjp.printTestVar();
gjp.setupRoot();
Output:
Starting
The testVar:3
Override root

Spock Metaprogramming getProperty()

I'd like to override the getProperty() method in a Spock test, per the documentation of get/setProperty. This is trivial in a normal Groovy class, but doesn't seem to work inside a Spock specification.
class MainSpec extends Specification {
#Override
def getProperty(String name) {
def value = metaClass.getProperty(this, name)
println "$name == $value"
return value
}
String foo = 'foo'
def test() {
expect:
foo
println foo
}
}
This example does not invoke the getProperty() method. It appears Spock is bypassing it somehow. Is there a way to hook into Spock's property resolution mechanism, or tell Spock to use my overridden method?
Yes, you can use "Expando" style, where the property should not be defined on the Spec class but is stored in a map (storage) and manipulated by setProperty and getProperty methods:
class MetaprogrammingSpec extends Specification {
def storage = [:]
#Override
def getProperty(String name) {
def value = storage[name]
println "$name == $value"
return value
}
#Override
void setProperty(String name, value) {
storage[name] = value
}
def test() {
when:
foo = 'bar'
then:
foo
println foo
}
}
Then the result is:
Update after comment: The getProperty() method is also called when you don't initialize the foo property. When you change the test in this way:
def test() {
expect:
foo
println foo
}
The result is as expected - getProperty() was called but test failes because foo is null:
It turns out that moving the property to a parent class is sufficient to make it pass through the getProperty() interceptor, even when the parent is another Specification class.
class BaseSpec extends Specification {
def foo = 'foo'
}
class MainSpec extends BaseSpec {
#Override
def getProperty(String name) {
def value = super.getProperty(name)
println "$name == $value"
return value
}
def test() {
expect:
foo
println foo
}
}

Call a method that inside a nested static class from the outer class by using a trait

I have a static class, 'Inner', and a nested static class, 'Deeper', in two different classes A,B. 'Inner' class implements the trait C that has a method called ping().
I want to execute the method hello() (that belongs to Deeper) from the ping() method in a way that each time I'll get either 'Hello A' or 'Hello B' according to the class that invoked the trait.
This is what I wrote (I'm using katalon-studio):
public class A
{
static class Inner implements C{
static class Deeper{
static void hello(){ println 'Hello A'}
}
}
}
public class B
{
static class Inner implements C{
static class Deeper{
static void hello(){ println 'Hello B'}
}
}
}
public static trait C {
static void ping() {
this.Deeper.hello()
}
}
A.Inner.ping()
B.Inner.ping()
I got the following error:
08-17-2018 04:46:57 PM - [ERROR] - Test Cases/V2/General/Draft FAILED
because (of) Variable 'Deeper' is not defined for test case.
Consider this code:
class A implements C {}
class B implements C {}
trait C {
def whoAmI() {
this.class.simpleName
}
}
// ---- main
assert "A" == new A().whoAmI()
assert "B" == new B().whoAmI()
Couldn't find an answer for this. However, using non-static classes, this could be implemented as follows:
public class A{
class Inner implements C{
A.Inner.Deeper d = new A.Inner.Deeper()
class Deeper {
void hello(){
println 'Hello A'
}
}
}
}
public class B{
class Inner implements C{
B.Inner.Deeper d = new B.Inner.Deeper()
class Deeper{
void hello(){
println 'Hello B'
}
}
}
}
public trait C{
public void ping(){
this.d.hello()
}
}
new A.Inner().ping()
new B.Inner().ping()

How to handle groovy ambiguous method overload gracefully

I know there are similar questions but the answers are not satisfying.
I get an Groovy ambiguous method overload error when calling a method with null as parameter.
e.g.:
class A{
sampleMethod (B bObj){
if(bObj == null) {
handleNullArgumentGracefully()
}
... do some cool stuff ...
}
sampleMethod (C cObj){
... do some other cool stuff ...
}
}
now when i call sampleMethod(null) groovy does not know which method it should invoke. Thats clear but is there a possibility to set one method of these two as default method to handle such null calls? I want to handle this on the callee side and not on the caller side (i do not want to cast something on the caller side)
UPDATE:
i found a solution how it could work but i donĀ“t know why: convert the non default method to a closure property
class app {
static void main(String[] args) {
def a = new A()
a.sampleMethod(new B())
a.sampleMethod(new C())
a.sampleMethod(null)
}
}
class A {
def sampleMethod(B bObj = null) {
if (bObj == null) {
println("handle null")
}
println("1")
}
def sampleMethod = { C cObj ->
println("2")
}
}
class B {
}
class C {
}
the following will fail with Ambiguous method overloading for method A#sampleMethod
class A{
def sampleMethod (Number o=null){
println "num $o"
}
def sampleMethod (String o){
println "str $o"
}
}
new A().sampleMethod(null)
this one will work (Object will be called for null):
class A{
def sampleMethod (Number o=null){
println "num $o"
}
def sampleMethod (String o){
println "str $o"
}
def sampleMethod(Object o){
println "obj $o"
}
}
new A().sampleMethod(null)
but i like this one:
class A{
def _sampleMethod (Number o){
println "num $o"
}
def _sampleMethod (String o){
println "str $o"
}
def sampleMethod(Object o){
if(o==null){
println "null"
return null
}else if(o instanceof Number){
return _sampleMethod ((Number) o)
}else if(o instanceof String){
return _sampleMethod ((String) o)
}
throw new IllegalArgumentException("wrong argument type: ${o.getClass()}")
}
}
new A().sampleMethod(null)

What's an elegant way to have a reusable metaclass code in Groovy?

I would like to apply a meta-programming transformation to some of my classes, let's say by adding printXxx methods, like this:
class Person {
String name
}
def p = new Person()
p.printName() // does something
I have a rough idea how this can be done once I have a metaclass:
Person.metaClass.methodMissing = { name, args ->
delegate.metaClass."$name" = { println delegate."${getPropName(name)}" }
delegate."$name"(*args)
}
Now how do I turn this code into a reusable "library"? I would like to do something like:
#HasMagicPrinterMethod
class Person {
String name
}
or
class Person {
String name
static {
addMagicPrinters()
}
}
Define the behaviour you want to add as a trait
trait MagicPrinter {
void printProperties() {
this.properties.each { key, val ->
println "$key = $val"
}
}
}
Then add this trait to a class
class Person implements MagicPrinter {
String name
}
Now use it!
new Person(name: 'bob').printProperties()
You can go for a mixin approach:
class AutoPrint {
static def methodMissing(obj, String method, args) {
if (method.startsWith("print")) {
def property = (method - "print").with {
it[0].toLowerCase() + it[1..-1]
}
"${obj.getClass().simpleName} ${obj[property]}"
}
else {
throw new NoSuchMethodException()
}
}
}
You can mix it with a static block:
class Person {
static { Person.metaClass.mixin AutoPrint }
String name
}
def p = new Person(name: "john doe")
try {
p.captain()
assert false, "should've failed"
} catch (e) {
assert true
}
assert p.printName() == "Person john doe"
Or with expandoMetaClass:
class Car {
String model
}
Car.metaClass.mixin AutoPrint
assert new Car(model: "GT").printModel() == "Car GT"
Because 7 months later is the new 'now' :-)

Resources