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