How to reference subclass from a static superclass method in Groovy - groovy

A simplified version of what I'm trying to do in Groovy:
class Animal {
static def echo() {
println this.name // ie "class.name"
}
}
class Dog extends Animal {
}
class Cat extends Animal {
}
Dog.echo()
Cat.echo()
// Output:
// => Animal
// => Animal
//
// What I want:
// => Dog
// => Cat
I think what I'm asking here is: when I call a static method on an object, and
the static method is defined in the object's superclass, is there a way to obtain
the actual type of the object?

A static method is not defined in the object context, but in the class context. You might get confused by the presence of this in the Groovy static method. However, it's only a syntactic sugar that eventually replaces this.name with Animal.class.name.
If you compile the Animal class from your example with a static compilation enabled, you will see that it compiles to the following Java equivalent (result after decompiling the .class file):
//
// 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.DefaultGroovyMethods;
public class Animal implements GroovyObject {
public Animal() {
MetaClass var1 = this.$getStaticMetaClass();
this.metaClass = var1;
}
public static Object echo() {
DefaultGroovyMethods.println(Animal.class, Animal.class.getName());
return null;
}
}
You can see that the following line in the echo method:
DefaultGroovyMethods.println(Animal.class, Animal.class.getName());
operates directly on the Animal class name. So from the echo method perspective, it doesn't matter how many classes extend it. As long as those classes invoke echo method defined in the Animal class, you will always see Animal printed as a result.
And there is even more than that. If you use the following compiler configuration script:
config.groovy
withConfig(configuration) {
ast(groovy.transform.CompileStatic)
ast(groovy.transform.TypeChecked)
}
and then compile the script (let's call it script.groovy) using this configuration option with the following command:
groovyc --configscript=config.groovy script.groovy
then you will see something like this after decompiling the .class file:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.Binding;
import org.codehaus.groovy.runtime.InvokerHelper;
public class script extends groovy.lang.Script {
public script() {
}
public script(Binding context) {
super(context);
}
public static void main(String... args) {
InvokerHelper.runScript(script.class, args);
}
public Object run() {
Animal.echo();
return Animal.echo();
}
}
You can see that even though you have invoked Dog.echo() and Cat.echo() in your Groovy script, the compiler replaced these calls with the double Animal.echo() invocation. It happened because calling this static method on any other subclass does not make any difference.
Possible solution: applying double dispatch
There is one way to get the expected output - override echo static method in Dog and Cat class. I can assume that your real method may do something more than the exemplary echo method you have shown above, so you might need to call the super echo method from a parent class. But... there are two problems: (1) you can't use super.echo() in the static context, and (2) it doesn't solve the problem, because parent method still operates in the Animal class context.'
To solve this kind of issue you might want to mimic a technique called double dispatch. In short - when we don't have information about the caller in the method that was called, let's allow the caller to pass this information with the method call. Consider the following example:
import groovy.transform.CompileStatic
#CompileStatic
class Animal {
// This is a replacement for the previous echo() method - this one knows the animal type from a parameter
protected static void echo(Class<? extends Animal> clazz) {
println clazz.name
}
static void echo() {
echo(Animal)
}
}
#CompileStatic
class Dog extends Animal {
static void echo() {
echo(Dog)
}
}
#CompileStatic
class Cat extends Animal {
static void echo() {
echo(Cat)
}
}
Animal.echo()
Dog.echo()
Cat.echo()
This may sound like a boilerplate solution - it requires implementing echo method in each subclass. However, it encapsulates the echo logic in the method that requires Class<? extends Animal> parameter, so we can let every subclass to introduce their concrete subtype. Of course, this is not a perfect solution. It requires implementing echo method in each subclass, but there is no other alternative way. Another problem is that it doesn't stop you from calling Dog.echo(Animal) which will cause the same effect as calling Animal.echo(). This double dispatch like approach is more like introducing a shorthand version of echo method which uses the common static echo method implementation for simplicity.
I don't know if this kind of approach solves your problem, but maybe it will help you find a final solution.

Related

Riddle me this: Inconsistent groovy meta programming behaviour

I stumbled across this when updating a large app from groovy 2 to 3 (and also to corresponding newer spock and geb versions).
This code behaves strange and also a different kind of strange in groovy 2 versus groovy 4.
I think we are running without "indy" here. I guess because all the transitive dependencies of our large app bring in specific groovy jars without indy. I should probably goe through them carefully and adapt our gradle build so that only "indy" versions of all jars are picked.
class A {
def foo() {
bar('hello')
beep(Optional.of('hello'))
}
protected void bar(String value) { println 'A.bar' }
protected void beep(Optional<String> value) { println 'A.beep' }
}
class B extends A {
protected void bar(String value) { println 'B.bar' }
protected void beep(Optional<String> value) { println 'B.beep' }
}
class C extends B implements GroovyInterceptable {
def invokeMethod(String name, Object args) {
super."$name"(*args)
}
}
static void main(String[] args) {
new C().foo()
println '---'
C c = new C()
c.bar('hello')
c.beep(Optional.of('hello'))
}
Output for groovy 2.5.15:
B.bar
A.beep
---
A.bar
A.beep
Output for groovy 4.0.0:
A.bar
A.beep
---
A.bar
A.beep
What I would have expected:
B.bar
B.beep
---
B.bar
B.beep
What's going on here? Bug or some strange, but expected corner case?
Where is the difference in behavior in between groovy 2 and 4 documented?
In our real app there was a difference already in between groovy 2 and 3 but I have been unable so far to create example code for that.
Is there a way to call the original method inside of invokeMethod? (Can't find anything in the docs, which are very sparse btw.)
I get your 3.0.9 output for Groovy 2.5.16, 3.0.10 and 4.0.1 -- indy enabled for all three.
Your implementation of invokeMethod relies on the behavior of ScriptBytecodeAdapter#invokeMethodOnSuperN which is what is behind super."$name"(*args). When handling "bar" message, the meta-method index has B.bar(java.lang.String) for "this" and B.super$2$bar(java.lang.String) for "super". super$2$bar is a meta-object protocol (MOP) method that provides the necessary INVOKESPECIAL instruction to reach A#bar(java.lang.String).
If you want the output of all calls to be from B then you can use this."$name"(*args) instead. In your specific case, there is no need to implement C as GroovyInterceptable and to try and route "foo", "bar" and "beep" yourself.
You can make your code produce the expected output by making the B class compiled statically:
import groovy.transform.CompileStatic
class A {
def foo() {
bar('hello')
beep(Optional.of('hello'))
}
protected void bar(String value) { println 'A.bar' }
protected void beep(Optional<String> value) { println 'A.beep' }
}
#CompileStatic
class B extends A {
protected void bar(String value) { println 'B.bar' }
protected void beep(Optional<String> value) { println 'B.beep' }
}
class C extends B implements GroovyInterceptable {
def invokeMethod(String name, Object args) {
super."$name"(*args)
}
}
static void main(String[] args) {
new C().foo()
println '---'
C c = new C()
c.bar('hello')
c.beep(Optional.of('hello'))
}
Output:
B.bar
B.beep
---
B.bar
B.beep
As it was mentioned by emilies in his answer, in the MOP use case scenario something like this happens:
c.bar('Hello')
invokeMethod('bar', ['Hello'] as Object[])
super."bar"(['Hello'] as Object[])
This super."bar"(['Hello'] as Object[]) is represented by B.super$2$bar(java.lang.String) method object which forces A.bar(java.lang.String) to be invoked right in the next call frame.
However, if you make the B class to be compiled statically, the method that is found to satisfy the super."bar"(['Hello'] as Object[]) expression, in that case, is B.bar(java.lang.String), and thus it gets invoked directly.
Regarding the differences between Groovy 2.5 and Groovy >=3.0, it looks like you have encountered a compiler bug. The bar('hello') inside the A.foo() method ignores the MOP and goes directly to this.bar(java.lang.String) which in this case is B.bar(java.lang.String).
It looks like it happens for the java.lang.String type (didn't check other types). However, when the type is java.util.Optional, then a call like beep(Optional.of('Hello')) inside the A.foo() method goes through the MOP and thus it discovers B.super$2$beep(java.util.Optional) method to be invoked:

Create a new class "on the fly"?

... specifically in Groovy (hence tag)?
In Java you can't do this... but in dynamic languages (e.g. Python) you typically can.
An attempt to do something like in the given block of a Spock feature (i.e. test method) meets with, in Eclipse:
Groovy:Class definition not expected here. Please define the class at
an appropriate place or perhaps try using a block/Closure instead.
... an "appropriate" place would obviously be outside the feature. This would be clunky and not groovy. Having used Groovy for a few months now I get a feel for when Groovy should offer something groovier.
So say I'd like to extend my abstract class AbstractFoo and make a new subclass Foo, in my feature, is there any way to "use a block/Closure" to achieve something like that?
You can simply create an anonymous class by instantiating AbstractFoo and providing inline implementation of abstract methods. Consider following example:
abstract class AbstractFoo {
void bar() {
println text()
}
abstract String text()
}
def foo1 = new AbstractFoo() {
#Override
String text() {
return "Hello, world!"
}
}
def foo2 = new AbstractFoo() {
#Override
String text() {
return "Lorem ipsum dolor sit amet"
}
}
foo1.bar()
foo2.bar()
Both foo1 and foo2 implement AbstractFoo and they provide different implementation of text() method that results in different bar() method behavior. Running this Groovy script produces following output to the console:
Hello, world!
Lorem ipsum dolor sit amet
It's nothing Groovy-specific, you can achieve exactly the same behavior with Java. However you can make it a little bit more "groovier" by casting a closure to a AbstractFoo class, something like this:
def foo3 = { "test 123" } as AbstractFoo
foo3.bar()
In this case closure that returns "test 123" provides an implementation for an abstract text() method. It works like that if your abstract class has only single abstract method.
Abstract class with multiple abstract methods
But what happens if an abstract class has multiple abstract methods we want to implement on fly? In this case we can provide implementation of this methods as a map, where keys are names of abstract methods and values are closures providing implementation. Let's take a look at following example:
abstract class AbstractFoo {
abstract String text()
abstract int number()
void bar() {
println "text: ${text()}, number: ${number()}"
}
}
def foo = [
text: { "test 1" },
number: { 23 }
] as AbstractFoo
foo.bar()
This example uses an abstract class with two abstract methods. We can instantiate this class by casting a map of type Map<String, Closure<?>> to AbstractFoo class. Running this example produces following output to the console:
text: test 1, number: 23
Creating non-anonymous classes on fly in Groovy
Groovy also allows you to create a class e.g. from a multiline string using GroovyClassLoader.parseClass(input) method. Let's take a look at following example:
abstract class AbstractFoo {
void bar() {
println text()
}
abstract String text()
}
def newClassDefinitionAsString = '''
class Foo extends AbstractFoo {
String text() {
return "test"
}
}
'''
def clazz = new GroovyClassLoader(getClass().getClassLoader()).parseClass(newClassDefinitionAsString)
def foo = ((AbstractFoo) clazz.newInstance())
foo.bar()
Here we are defining a non-anonymous class called Foo that extends AbstractFoo and provides a definition of test() method. This approach is pretty error prone, because you define a new class as String, so forget about any IDE support in catching errors and warnings.
Providing a subclass in a test specification
Your initial question mentioned about an attempt to create a class for a specification in a given: Spock block. I would strongly suggest using the simplest available tool - creating a nested private static class so you can easily access it inside your test and you don't expose it outside the test. Something like this:
class MySpec extends Specification {
def "should do something"() {
given:
Class<?> clazz = Foo.class
when:
//....
then:
///....
}
private static class Foo extends AbstractFoo {
}
}

Closure Coercion not working in certain case

I'm calling a Java method from Groovy which expects an instance of a SAM interface as a parameter.
Normally Groovy is happy with passing in a closure in these cases, and will coerce it accordingly HOWEVER in this case, the interface extends another one and overrides the single method.
Note - It still only has one method, but it's been overriden.
In this instance Groovy doesn't automatically coerce the closure and the only way I seem to be able to call it is by using "AS".
I'm publishing an API to help kids to learn code and really don't want them to have to use "AS" because it would complicate things.
Here's some code that shows the issue...
Java
public interface BaseHandler<T> {
public void handle(T test);
}
public interface Handler extends BaseHandler<String> {
public void handle(String test);
}
public class LibraryClass {
public void method(Handler handler) {
handler.handle("WORLD!");
}
}
Groovy
LibraryClass bar = new LibraryClass();
bar.method({ name -> println "HELLO " + name})
Error
Caught: groovy.lang.MissingMethodException: No signature of method: Bar.doIt() is applicable for argument types: (testClosures$_run_closure1) values: [testClosures$_run_closure1#fe63b60]
Any help on how to get around this without using "AS" would be hugely appreciated
Groovy wants to implement the interface by coercion, but doesn't know which interface method it should implement. As there are 2:
the handle(String test) and a second one: handle(String test) (of the baseHandler)
The solution is to remove the handle(String test) from the handler (it adds nothing as the BaseHandler posesses this method already thanks to the generics).
Like this it works correctly:
public interface BaseHandler<T> {
public void handle(T test);
}
public interface Handler extends BaseHandler<String> {
}
public class LibraryClass {
public void method(Handler handler) {
handler.handle("WORLD!");
}
}

groovy: variable scope in closures in the super class (MissingPropertyException)

I have the impression that closures run as the actual class being called (instead of the implementing super class) and thus break when some variables are not visible (e.g. private in the super class).
For example
package comp.ds.GenericTest2
import groovy.transform.CompileStatic
#CompileStatic
class ClosureScopeC {
private List<String> list = new ArrayList<String>()
private int accessThisPrivateVariable = 0;
void add(String a) {
list.add(a)
println("before ${accessThisPrivateVariable} ${this.class.name}")
// do something with a closure
list.each {String it ->
if (it == a) {
// accessThisPrivateVariable belongs to ClosureScopeC
accessThisPrivateVariable++
}
}
println("after ${accessThisPrivateVariable}")
}
}
// this works fine
a = new ClosureScopeC()
a.add("abc")
a.add("abc")
// child class
class ClosureScopeD extends ClosureScopeC {
void doSomething(String obj) {
this.add(obj)
}
}
b = new ClosureScopeD()
// THIS THROWS groovy.lang.MissingPropertyException: No such property: accessThisPrivateVariable for class: comp.ds.GenericTest2.ClosureScopeD
b.doSomething("abc")
The last line throws a MissingPropertyException: the child class calls the "add" method of the super class, which executes the "each" closure, which uses the "accessThisPrivateVariable".
I am new to groovy, so I think there must be an easy way to do this, because otherwise it seems that closures completely break the encapsulation of the private implementation done in the super class ... this seems to be a very common need (super class implementation referencing its own private variables)
I am using groovy 2.1.3
I found this to be a good reference describing how Groovy variable scopes work and applies to your situation: Closure in groovy cannot use private field when called from extending class
From the above link, I realized that since you have declared accessThisPrivateVariable as private, Groovy would not auto-generate a getter/setter for the variable. Remember, even in Java, private variables are not accessible directly by sub-classes.
Changing your code to explicitly add the getter/setters, solved the issue:
package com.test
import groovy.transform.CompileStatic
#CompileStatic
class ClosureScopeC {
private List<String> list = new ArrayList<String>()
private int accessThisPrivateVariable = 0;
int getAccessThisPrivateVariable() { accessThisPrivateVariable }
void setAccessThisPrivateVariable(int value ){this.accessThisPrivateVariable = value}
void add(String a) {
list.add(a)
println("before ${accessThisPrivateVariable} ${this.class.name}")
// do something with a closure
list.each {String it ->
if (it == a) {
// accessThisPrivateVariable belongs to ClosureScopeC
accessThisPrivateVariable++
}
}
println("after ${accessThisPrivateVariable}")
}
}
// this works fine
a = new ClosureScopeC()
a.add("abc")
a.add("abc")
// child class
class ClosureScopeD extends ClosureScopeC {
void doSomething(String obj) {
super.add(obj)
}
}
b = new ClosureScopeD()
b.doSomething("abc")
There is a simpler way, just make the access modifier (should rename the property really) to protected, so the sub-class has access to the property.. problem solved.
protected int accessThisProtectedVariable
To clarify on your statement of concern that Groovy possibly has broken encapsulation: rest assured it hasn't.
By declaring a field as private, Groovy is preserving encapsulation by intentionally suspending automatic generation of the public getter/setter. Once private, you are now responsible and in full control of how or if there is a way for sub-classes (protected) or all classes of objects (public) to gain access to the field by explicitly adding methods - if that makes sense.
Remember that by convention, Groovy ALWAYS calls a getter or setter when your codes references the field. So, a statement like:
def f = obj.someField
will actually invoke the obj.getSomeField() method.
Likewise:
obj.someField = 5
will invoke the obj.setSomeField(5) method.

Groovy expando metaclass

I've developed a Class that has some methods that augment Integer, it mainly lets me do this:
def total = 100.dollars + 50.euros
Now I have to extend Integer.metaClass doing something like this:
Integer.metaClass.getDollars = {->
Money.Dollar(delegate)
}
I tried putting that at the bottom of the file, before the Money class declaration, but the compiler says that a class Named Money already exists, I know why it happens (because groovy creates a class with the name of the file with an empty static void main to run this code).
I also tried using a static block inside the class like this:
static {
Integer.metaClass.getDollars = {->
Money.Dollar(delegate)
}
}
This neither works.
A third solution would be to change the file name (like MoneyClass.groovy) and keep the class name (class Money) but that seems a bit weird.
Is there anything else I can do? Thanks.
Just put it in any method of any class maybe a bean TypeEnhancer.groovy:
public class TypeEnhancer {
public void start() {
Integer.metaClass.getDollars() = {-> Money.Dollar(delegate) }
}
public void stop() {
Integer.metaClass = null
}
}
Just create and initalize by calling start(): new TypeEnhancer().start();.
To disable the enhancement, call new TypeEnhancer().stop();. The bean can also used as Spring bean.

Resources