找不到匿名的匹配构造函数 class

Could not find matching constructor for anonymous class

考虑以下代码示例

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.<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)

对我来说,匿名者应该与命名者相同class。但事实证明 Groovy 对他们的态度不同。我想知道如何解决它。谢谢。

您看到此错误是因为动态映射构造函数的性质 - 它不会显式添加到生成的 classes 中,而是通过 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

当你反编译生成的A.class文件时,你会得到这样的结果:

//
// 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;
    }
}

这个class只有一个无参构造函数。当你反编译test.class文件(编译Groovy脚本文件)时,你会看到这样的东西:

//
// 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 {
    }
}

看看对象 a1a2 是如何初始化的。 a1 对象按以下方式初始化:

Object a1 = var1[1].callConstructor(B.class, ScriptBytecodeAdapter.createMap(new Object[]{"data", 1}));

它使用CallSite.callConstructor()方法模仿A class中不存在的地图构造函数。如果我们查看对象 a2 的初始化方式,我们会发现:

Object a2 = new test.1(ScriptBytecodeAdapter.createMap(new Object[]{"data", 2}));

我们可以看到 Groovy 在匿名 class 的情况下(这根本不是匿名的 - Groovy 无论如何都会生成一个 class),Groovy 使用直接构造函数调用。它失败了,因为父 class.

中没有 A(LinkedHashMap) 构造函数

解决方案

幸运的是,这个问题有一个解决方案 - 您可以使用 @MapConstructor@InheritConstructors 注释强制在 A class 中创建地图构造函数,并在Bclass继承这个构造函数。看看这个工作示例:

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

唯一的要求是至少使用引入了 @MapConstructor 注释的 Groovy 2.5 版本。