Groovy Collection设置内部方法问题为空

时间:2018-11-12 10:38:44

标签: groovy pass-by-reference

我有一个带有函数func(Map data)的Groovy脚本,该函数接受一个映射,并使用一个空映射-data = [:]重新初始化传递的变量。我面临的问题是将非空映射传递给此函数不会覆盖具有空映射的映射。为什么会这样?

这是我的Groovy代码段:

Map x = [data1 : 10, data2 : 20]

def func(Map data) {
    data = [:]
}

def func2(Map data) {
    data.clear()
}

func(x)

// Setting x = [:] outside function does set x to empty

print x // prints [data1:10, data2:20]

func2(x)

print x // prints [:] (as .clear() is working)

顺便说一句:列表的行为相同。

1 个答案:

答案 0 :(得分:2)

之所以会这样,是因为Groovy编译器在data函数内创建了一个新的局部变量func(Map data)。看一下反编译的代码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import groovy.lang.Binding;
import groovy.lang.Script;
import java.util.Map;
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();
    }

    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();
        Map x = ScriptBytecodeAdapter.createMap(new Object[]{"data1", 10, "data2", 20});
        var1[1].callCurrent(this, x);
        var1[2].callCurrent(this, x);
        var1[3].callCurrent(this, x);
        return var1[4].callCurrent(this, x);
    }

    public Object func(Map data) {
        CallSite[] var2 = $getCallSiteArray();
        var2[5].callCurrent(this, "test");
        Map var3 = ScriptBytecodeAdapter.createMap(new Object[0]);
        return var3;
    }

    public Object func2(Map data) {
        CallSite[] var2 = $getCallSiteArray();
        return var2[6].call(data);
    }
}

检查字节码级别上的func方法代表什么:

public Object func(Map data) {
    CallSite[] var2 = $getCallSiteArray();
    var2[5].callCurrent(this, "test");
    Map var3 = ScriptBytecodeAdapter.createMap(new Object[0]);
    return var3;
}

您可以看到以下Groovy代码:

data = [:]

被翻译成这样:

Map var3 = ScriptBytecodeAdapter.createMap(new Object[0]);

但是,这种行为不仅特定于Groovy,而且还特定于Java。看看Java中非常相似的示例:

import java.util.HashMap;
import java.util.Map;

final class TestJava {

    public static void main(String[] args) {
        Map<String, Object> map = new HashMap<>();
        map.put("test", "foo");

        func(map);

        System.out.println("map outside = " + map);
    }

    static void func(Map<String, Object> map) {
        map = new HashMap<>();
        map.put("1", 2);

        System.out.println("map inside = " + map);
    }
}

如果运行它,我们将看到类似于Groovy用例的东西:

map inside = {1=2}
map outside = {test=foo}

我们可以预期func方法应该覆盖map,但是这里没有发生。如果我们反编译类文件,我们将看到以下内容:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import java.util.HashMap;
import java.util.Map;

final class TestJava {
    TestJava() {
    }

    public static void main(String[] var0) {
        HashMap var1 = new HashMap();
        var1.put("test", "foo");
        func(var1);
        System.out.println("map outside = " + var1);
    }

    static void func(Map<String, Object> var0) {
        HashMap var1 = new HashMap();
        var1.put("1", 2);
        System.out.println("map inside = " + var1);
    }
}

从JRE的角度可以看出,我们正在创建一个新的HashMap存储为var1变量,而不是覆盖传递给该方法的var0变量。

顺便说一句,我使用的Java版本:OpenJDK 1.8.0_191