You see this error because of the nature of the dynamic map constructor - it is not added explicitly to the generated classes, but it is called through the CallSite.callConstructor(obj,map) method instead. However, there is a solution to that problem.
Consider the following exemplary test.groovy script:
class A {
int data
}
class B extends A {}
def a1 = new B(data: 1)
def a2 = new A(data: 2) {}
println a1
println a2
When you decompile generated A.class file, you will something like this:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import groovy.transform.Generated;
import groovy.transform.Internal;
import java.beans.Transient;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class A implements GroovyObject {
private int data;
@Generated
public A() {
CallSite[] var1 = $getCallSiteArray();
super();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
@Generated
@Internal
@Transient
public MetaClass getMetaClass() {
MetaClass var10000 = this.metaClass;
if (var10000 != null) {
return var10000;
} else {
this.metaClass = this.$getStaticMetaClass();
return this.metaClass;
}
}
@Generated
@Internal
public void setMetaClass(MetaClass var1) {
this.metaClass = var1;
}
@Generated
public int getData() {
return this.data;
}
@Generated
public void setData(int var1) {
this.data = var1;
}
}
This class has only one no-args constructor. When you decompile the test.class file (compiled Groovy script file), you will see something like this:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import groovy.lang.Binding;
import groovy.lang.Script;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class test extends Script {
public test() {
CallSite[] var1 = $getCallSiteArray();
super();
}
public test(Binding context) {
CallSite[] var2 = $getCallSiteArray();
super(context);
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].call(InvokerHelper.class, test.class, args);
}
public Object run() {
CallSite[] var1 = $getCallSiteArray();
Object a1 = var1[1].callConstructor(B.class, ScriptBytecodeAdapter.createMap(new Object[]{"data", 1}));
Object a2 = new test.1(ScriptBytecodeAdapter.createMap(new Object[]{"data", 2}));
var1[2].callCurrent(this, a1);
return var1[3].callCurrent(this, a2);
}
public class 1 extends A {
}
}
Take a look at how objects a1 and a2 are initialized. The a1 object is initialized in the following way:
Object a1 = var1[1].callConstructor(B.class, ScriptBytecodeAdapter.createMap(new Object[]{"data", 1}));
It uses the CallSite.callConstructor() method to mimic the map constructor which does not exist in the A class. If we look at how the object a2 is initialized we will find this:
Object a2 = new test.1(ScriptBytecodeAdapter.createMap(new Object[]{"data", 2}));
We can see that Groovy in the case of the anonymous class (which is not anonymous at all - Groovy generates a class anyway), Groovy uses a direct constructor call. And it fails, because there is no A(LinkedHashMap) constructor in the parent class.
Solution
Luckily, there is a solution to this problem - you can use @MapConstructor and @InheritConstructors annotations to force creating map constructor in the A class, and to inherit this constructor in the B class. Take a look at this working example:
import groovy.transform.InheritConstructors
import groovy.transform.MapConstructor
@MapConstructor
class A {
int data
}
@InheritConstructors
class B extends A {}
def a1 = new B(data: 1)
def a2 = new A(data: 2) {}
println a1
println a2
The only requirement is to use at least the Groovy 2.5 version which introduced the @MapConstructor annotation.