#Delegate class without default constructor - groovy

How can I create a delegate class in Groovy for a class which doesn't have a default constructor? I would like to decorate JUnit's ResultPrinter but am getting an error about the missing constructor.

I don't understand your issue. I just tried this with Java's Short — which also does not have a default constructor.
Everything worked as expected, except if you didn't initialize the delegated object, you get an NPE.
Is it possible you are using #Delegate incorrectly? Delegate doesn't decorate existing classes, it allows you to use an existing classes methods in your own class. It's like extend, but without the class inheritance.
Example code:
class Foo {
#Delegate Short num
String bar
String toString() { "$bar: $num" }
}
def f = new Foo(bar: 'bob', num: 34 as Short)
println f // OK
println f.doubleValue() // OK
f = new Foo()
println f.doubleValue() // NPE
(Alternatively, providing some useful information, such as the actual error and stacktrace, and example code, will get you more useful responses.)

Related

Why is Groovy metaClass .static changes using MOP not behaving as expected

I have set up a simple dummy class as follows, and used a static initialiser to update the metaClass:
class DynamicExtendableClass {
static String declaredStaticString = "declared static string"
static String getDeclaredMethodStaticString () {
"static method returning string"
}
static {
println "static initialiser - adding dynamic properties and methods to metaClass"
DynamicExtendableClass.metaClass.addedProperty = "added property to class metaClass"
DynamicExtendableClass.metaClass.getAddedMethod = { -> "added closure as method" }
DynamicExtendableClass.metaClass.static.getStaticAddedMethod = { -> "added closure as static method" }
}
}
I have a simple test case like this:
#Test
void testExtendedMetaClassStuff () {
DynamicExtendableClass testInstance = new DynamicExtendableClass()
assertEquals ("added property to class metaClass", testInstance.addedProperty)
assertEquals ("added closure as static method", testInstance.getStaticAddedMethod()) //calls getStaticAddedMethod - groovy trick
assertEquals ("added closure as method", testInstance.addedMethod) //works. calls getAddedMethod - groovy trick for getXxx as property
assertEquals ("added closure as static method", DynamicExtendableClass.staticAddedMethod ) //works class static class Closure
}
Which works only once you create a first instance of the class which forces a switch to ExpandoMetaClass for you.
If you don't do this first the default HandleMetaClassImpl doesn't work for this.
However to get this to work for static you have to create closure like getXxxx = {-> ...}, which if you call 'DynamicExtendableClass.staticAddedMethod' will sneakily invoke the closure for you.
However, there's not really a means to add a property capability here for '.static' as there is on the standard metaClass itself. All you can do is set a closure onto .static. Why is this?
The other problem is having to create an instance of the class first to force the switch to ExpandoMetaClass, is there not a simple way to force the metaClass change when declaring the class in the first class, before creating any instances ?
I want to add some static properties (later some methods maybe ) dynamically to a class, but all you can add is static closures, which is a little limiting on the scenario I had in mind.
PostScript
I managed to force a change of metaClass on class without having to create an instance, but it's a bit hard work:
#Test
void testMetaClassStatic () {
println DynamicExtendableClass.metaClass
MetaClassRegistry registry = GroovySystem.getMetaClassRegistry()
MetaClass origMC = registry.getMetaClass(DynamicExtendableClass)
assert origMC.getClass() == HandleMetaClass //default implementation
ExpandoMetaClass emc = new ExpandoMetaClass (DynamicExtendableClass, true, true)
emc.static.getStaticAddedMethod = {-> "static hello from my emc"}
emc.initialize()
registry.removeMetaClass(DynamicExtendableClass)
registry.setMetaClass(DynamicExtendableClass, emc)
assert DynamicExtendableClass.metaClass.getClass() == ExpandoMetaClass
assert DynamicExtendableClass.staticAddedMethod == "static hello from my emc"
registry.removeMetaClass(DynamicExtendableClass)
registry.setMetaClass(DynamicExtendableClass, origMC)
}
But doing this breaks my previously working tests (not sure why) with:
Could not initialize class extensible.DynamicExtendableClass
java.lang.NoClassDefFoundError: Could not initialize class extensible.DynamicExtendableClass
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:73)
at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:108)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:59)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:263)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:268)
at extensible.DynamicExtendableClassTest.testExtendedMetaClassStuff(DynamicExtendableClassTest.groovy:22)
at ...
Another postscript
I did a little exploration with a debugger. 1st the metaClass.static returns a class of type ExpandoMetaClass.ExpandoMetaProperty which of itself isn't terribly useful. You can do a direct .#this$0 field access however which just points the same metaClass instance as the target class you start with.
Therefore ignoring this you can do a direct field grab on <yourClass>.metaClass.#expandoProperties (I tried to get this via reflection using:
PropertyValue expandoProperties = clazz.metaClass.getMetaPropertyValues().find{it.name == 'expandoProperties'}
List<MetaBeanProperty> MBprops2= properties.getValue()
Map m2 = MBprops.findAll{Modifier.isPublic(it.modifiers)}.collectEntries{[(it.name), it.getProperty(clazz)] }
but it doesn't get the same content as the direct field access does.
The direct field access returns a Map where the key is the string value of any added closures or properties added dynamically to the metaClass, and the value is a MetaBeanProperty reference.
On that MetaBeanProperty you can invoke the getProperty (object) using with the class metaClass or per instance metaClass - and it returns the value of that property (whether it's just a closure or a real property) for you. You can also test whether its static or not:
Map m4 = thisMc.#expandoProperties
MetaBeanProperty asm = m4['addedStaticMethod']
def val2 = asm.getProperty(clazz)
boolean isstatic = Modifier.isStatic(asm.modifiers)
Kind of brutal but it sort of works if you want to dynamically query the dynamic editions to the metaclass.
The problem of forcing the switch from default metaClass to the ExpandoMetaClass remains a problem. The best way seems to create a throw away class instance as this does the one time switch for you.
I tried to force this myself using the metaClass registry which you can do, but then the future create new instance for your class seems to stop working ie. doing somethings like this and putting the original back afterwords seems to break any future new <MyClass>() calls.
MetaClassRegistry registry = GroovySystem.getMetaClassRegistry()
MetaClass origMC = registry.getMetaClass(DynamicExtendableClass)
assert origMC.getClass() == MetaClassImpl //default implementation
def constructors = MetaClassImpl.getConstructors()
ExpandoMetaClass emc = new ExpandoMetaClass (DynamicExtendableClass, true, true)
emc.static.getStaticAddedMethod = {-> "static hello from my emc"}
emc.constructor = { new DynamicExtendableClass() }
emc.initialize()
registry.removeMetaClass(DynamicExtendableClass)
registry.setMetaClass(DynamicExtendableClass, emc)
assert DynamicExtendableClass.metaClass.getClass() == ExpandoMetaClass
assert DynamicExtendableClass.staticAddedMethod == "static hello from my emc"
registry.removeMetaClass(DynamicExtendableClass)
registry.setMetaClass(DynamicExtendableClass, origMC)

groovy immutable object with parent class

I have two immutable groovy classes that have a few shared values that I'm trying to abstract to a parent class. However when I create the following, the second test case always fails. Although everything compiles correctly and no error is thrown at runtime, when I assign the parent property int he constructor, it is never set, resulting in a null value. I havent found any documentation that forbids this, but I'm wondering is this even possible? I've tried a number of configuration of Annotations and class-types (e.g. removing abstract from the parent) but nothing seems to work short of just removing the #Immutable tag altogether.
abstract class TestParent {
String parentProperty1
}
#ToString(includeNames = true)
#Immutable
class TestChild extends TestParent {
String childProperty1
String childProperty2
}
class TestCase {
#Test
void TestOne() {
TestChild testChild = new TestChild(
childProperty1: "childOne",
childProperty2: "childTwo",
parentProperty1: "parentOne"
)
assert testChild
assert testChild.parentProperty1
}
}
Based on the code for the ImmutableASTTransformation, the Map-arg constructor added by the createConstructorMapCommon method does not include a call to super(args) in the method body.
which means that immutable classes are self contained by default
Now if you want to do it you need to use composition instead of inheritance and this is an example of how you can do it :
import groovy.transform.*
#TupleConstructor
class A {
String a
}
#Immutable(knownImmutableClasses=[A])
class B {
#Delegate A base
String b
}
def b = new B(base: new A("a"), b: "b")
assert b.a
i hope this will help :)

Groovy - Is it possible to pass a class as a parameter?

This is just a very basic example of what I want to do. There's a bit more that goes on in the foobar method, but it's the gist of what I'm doing. It obviously doesn't work, since it fails to compile, but I'm wondering if I'm just passing the class incorrectly or using the 'className' parameter in the wrong way. I know I can rework it to take the string of the class name and just match it, but it seems a shame to do that. This would be so nice and DRY.
class Foo {
String name
}
class Bar {
String name
}
def foobar(field, className) {
def instance = className.findByName(jsonParams.field)
if(!instance) {
instance = new className(name: jsonParams.field)
}
return instance
}
foobar(foos, Foo)
foobar(bars, Bar)
I don't know much Java or Groovy, so I'm not sure what's possible vs impossible yet. Feel free to just tell me "No." I've tried googling and haven't found anything that really answers the question for me. A simple no would be great at this point haha.
Yes, it is possible to pass class as argument - in Groovy, classes are first class citizens (see this thread for more detail).
This construct: instance = new className(name: jsonParams.field) actually tries to create an instance of class named className, not of the class referenced by this variable. To make it compile, you need to call Class.newInstance:
class Foo {
String name
}
class Bar {
String name
}
def foobar(String name,Class clazz) {
def instance = clazz.findByName(name)
if(!instance) {
instance = clazz.newInstance(name:name)
}
return instance
}
foobar('foo', Foo)
foobar('bar', Bar)
​
I'm not entirely sure what you want to achieve with the findByName method, though - neither Foo nor Bar have a static method named findByName as far as I can tell.

Groovy type definitions

class GroovyClass {
def aVariable
void setAVariable(aVariable)
{
this.aVariable = aVariable;
}
}
My understanding was that we don't need to specify the type of a variable in a groovy class. But Groovy compiler complains if I declare 'aVariable' , why isn't it considered as a typeless variable with default accessibility ? Should every variable be defined with a def in Groovy both local and class ? Why is it that the function definition doesn't have to begin with a def ? and when I'm passing in a variable to the setter, it doesn't need any def in there ?
That code works fine. What do you mean by "Groovy compiler complains"?
You can define that function with a def if you wanted, and it would return aVariable (as that is what the assignment operatop returns), however, it wouldnt be following the standard for Java Beans in that setters should return null
Given that however, I can run:
a = new GroovyClass()
a.aVariable = 3
And it works fine
Edit
Basically, it's all down to the Groovy parser. The parser expects some sort of list of 1..N keywords defining it's type or visibility, and then a name for the variable. So the following are all valid:
class OkA {
def aValue
}
class OkB {
private aValue
}
class OkC {
private String aValue
}
But you cannot just (with the current parser) say:
class BadA {
aValue
}
Thinking about it, there's no reason I can currently think of for this restriction (as you can declare vars without def in Groovy), but the restriction is there, so you need to type def when defining class attributes.

What is the Groovy way to copy arguments to properties in a Groovy class?

Given an object with properties and a constructor, I wish to copy the constructor arguments into properties, and then do some additional work in the Constructor.
import groovy.transform.TupleConstructor
#TupleConstructor
class Thing{
def one
def two
public Thing(one, two){
doSomething()
}
def doSomething(){
println "doing something with one : $one and two: $two"
}
}
println new Thing(1, 2).dump()
This will successfully copy the args to the properties if I do nothing else in the constructor, but if I call "doSomething()" in the constructor, the properties are not copied.
I'm seeking "The Groovy" Way for copying args to properties.
As tim_yates mentioned, the TupleConstructor AST transformation won't do anything if you have another constructor defined (you can blame this line of code =P). If you need to run some other code in the construction of the object, you may add that in a static factory method and use that instead of the tuple constructor directly:
import groovy.transform.TupleConstructor
#TupleConstructor
class Thing {
def one
def two
def doSomething(){
println "doing something with one : $one and two: $two"
}
static create(...args) {
def thing = new Thing(*args)
thing.doSomething()
thing
}
}
println Thing.create(1, 2).dump()
Notice that i'm using a variable-argument static method to receive an arbitrary number of parameters and then calling the tuple constructor with those parameters (used the "spread" (*) operator for that).
Unfortunately, the TupleConstructor AST transform does not seem to have an option for adding the tuple constructor as private, which would be useful in this case.
If you use TupleConstructor, it will not run if you have defined your own constructor.
And as you have defined a duplicate constructor to the one TupleConstructor will generate in the bytecode, even doing #TupleConstructor( force=true ) won't help you as you will just get a java.lang.ClassFormatError: Duplicate method name&signature in class file Thing
The best I can think of at the moment is to do:
class Thing{
def one
def two
public Thing( Map params ){
this.class.declaredFields.grep { !it.synthetic }.name.each { name ->
this[ name ] = params[ name ]
}
doSomething()
}
def doSomething(){
println "doing something with one : $one and two: $two"
}
}
println new Thing(one:1, two:2).dump()
Though there is probably a better way that I'm missing

Resources