Эта оптимизация существует, потому что следующее выражение:
animal1 == animal1
Groovy переводит в следующий вызов метода:
ScriptBytecodeAdapter.compareEqual(animal1, animal1)
Теперь, если мы посмотрим на этот методИсходный код мы обнаружим, что на первом этапе этот метод использует сравнение ссылок на старые добрые объекты Java - если обе стороны выражения указывают на одну и ту же ссылку, он просто возвращает true
и equals(o)
или compareTo(o)
(в случае сравнения объектов, которые реализуют Comparable<T>
интерфейс) методы не вызываются:
public static boolean compareEqual(Object left, Object right) {
if (left==right) return true;
Class<?> leftClass = left==null?null:left.getClass();
Class<?> rightClass = right==null?null:right.getClass();
// ....
}
В вашем случае обе переменные left
и right
указывают на одну и ту же ссылку на объект, поэтомупервая проверка в методе соответствует и true
возвращается.
Если вы установите точку останова в этом месте (ScriptBytecodeAdapter.java
строка 685), вы увидите, что отладчик достигает этой точки, и он возвращает true
изпервая строка этого метода.
Декомпиляция Groovy-кода
В качестве приятного упражнения вы можете взглянуть на следующий пример.Это простой скрипт Groovy (называемый Animal_script.groovy
), который использует класс Animal.java
и выполняет сравнение объектов:
def animal1 = new Animal("Fluffy", 4)
def animal2 = new Animal("Fluffy", 4)
def animal3 = new Animal("Snoopy", 4)
println animal1 == animal1
Если вы скомпилируете его и откроете файл Animal_script.class
в IntelliJ IDEA (так что он можетбыть декомпилированным обратно в Java), вы увидите что-то вроде этого:
//
// 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 Animal_script extends Script {
public Animal_script() {
CallSite[] var1 = $getCallSiteArray();
}
public Animal_script(Binding context) {
CallSite[] var2 = $getCallSiteArray();
super(context);
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].call(InvokerHelper.class, Animal_script.class, args);
}
public Object run() {
CallSite[] var1 = $getCallSiteArray();
Object animal1 = var1[1].callConstructor(Animal.class, "Fluffy", 4);
Object animal2 = var1[2].callConstructor(Animal.class, "Fluffy", 4);
Object animal3 = var1[3].callConstructor(Animal.class, "Snoopy", 4);
return var1[4].callCurrent(this, ScriptBytecodeAdapter.compareEqual(animal1, animal1));
}
}
Как вы можете видеть, animal1 == animal1
во время выполнения Java рассматривается как ScriptBytecodeAdapter.compareEqual(animal1, animal1))
.
Надеюсь, это поможет.