Пустой параметр Groovy Collection внутри вопроса метода - PullRequest
0 голосов
/ 12 ноября 2018

У меня есть скрипт Groovy с функцией func(Map data), которая берет карту и повторно инициализирует переданную переменную с пустой картой - 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 голосов
/ 12 ноября 2018

Это происходит потому, что 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

...