考虑以下代码示例
class A {
int data
}
class B extends A {}
def o1 = new B(data: 1)
// This works correctly.
def o2 = new A(data:1) {}
// This will throw the following error
// Exception thrown
//
// groovy.lang.GroovyRuntimeException: Could not find matching constructor for: A(LinkedHashMap)
// at ConsoleScript2$1.<init>(ConsoleScript2)
// at ConsoleScript2.run(ConsoleScript2:11)
// at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
// at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
// at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
Run Code Online (Sandbox Code Playgroud)
对我来说,匿名类应该与命名类相同。但事实证明,Groovy 对待它们的方式有所不同。我想知道如何解决它。谢谢。
您会看到此错误,因为动态映射构造函数的性质 - 它没有显式添加到生成的类中,而是通过方法调用CallSite.callConstructor(obj,map)。然而,这个问题有一个解决方案。
考虑以下示例性test.groovy脚本:
class A {
int data
}
class B extends A {}
def a1 = new B(data: 1)
def a2 = new A(data: 2) {}
println a1
println a2
Run Code Online (Sandbox Code Playgroud)
当你反编译生成的A.class文件时,你会看到这样的内容:
class A {
int data
}
class B extends A {}
def a1 = new B(data: 1)
def a2 = new A(data: 2) {}
println a1
println a2
Run Code Online (Sandbox Code Playgroud)
该类只有一个无参数构造函数。当你反编译该test.class文件(编译后的Groovy脚本文件)时,你会看到类似这样的内容:
//
// 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;
}
}
Run Code Online (Sandbox Code Playgroud)
看一下对象a1和a2是如何初始化的。该a1对象通过以下方式初始化:
//
// 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 {
}
}
Run Code Online (Sandbox Code Playgroud)
它使用该CallSite.callConstructor()方法来模仿类中不存在的映射构造函数A。如果我们看看对象a2是如何初始化的,我们会发现:
Object a1 = var1[1].callConstructor(B.class, ScriptBytecodeAdapter.createMap(new Object[]{"data", 1}));
Run Code Online (Sandbox Code Playgroud)
我们可以看到,Groovy 在匿名类的情况下(根本不是匿名的 - Groovy 无论如何都会生成一个类),Groovy 使用直接构造函数调用。A(LinkedHashMap)它失败了,因为父类中没有构造函数。
幸运的是,这个问题有一个解决方案——你可以使用@MapConstructor和@InheritConstructors注解来强制在A类中创建映射构造函数,并在类中继承这个构造函数B。看看这个工作示例:
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
Run Code Online (Sandbox Code Playgroud)
唯一的要求是至少使用引入@MapConstructor注释的 Groovy 2.5 版本。
| 归档时间: |
|
| 查看次数: |
4899 次 |
| 最近记录: |