gpt4 book ai didi

java - Groovy == 运算符未达到 Java equals(o) 方法 - 这怎么可能?

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:54:37 25 4
gpt4 key购买 nike

我有以下 Java 类:

import org.apache.commons.lang3.builder.EqualsBuilder;

public class Animal {

private final String name;
private final int numLegs;

public Animal(String name, int numLegs) {
this.name = name;
this.numLegs = numLegs;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}

if (o == null || getClass() != o.getClass()) {
return false;
}

Animal animal = (Animal)o;

return new EqualsBuilder().append(numLegs, animal.numLegs)
.append(name, animal.name)
.isEquals();
}

}

以及以下 Spock 测试:

import spock.lang.Specification

class AnimalSpec extends Specification {

def 'animal with same name and numlegs should be equal'() {
when:
def animal1 = new Animal("Fluffy", 4)
def animal2 = new Animal("Fluffy", 4)
def animal3 = new Animal("Snoopy", 4)
def notAnAnimal = 'some other object'
then:
animal1 == animal1
animal1 == animal2
animal1 != animal3
animal1 != notAnAnimal
}

}

然后在运行覆盖时,第一个语句animal1 == animal1没有到达equals(o)方法:

Line 16 not covered by test

Groovy/Spock 没有运行第一条语句有什么原因吗?我假设进行了微优化,但是当我犯错误

@Override
public boolean equals(Object o) {
if (this == o) {
return false;
}

if (o == null || getClass() != o.getClass()) {
return false;
}

Animal animal = (Animal)o;

return new EqualsBuilder().append(numLegs, animal.numLegs)
.append(name, animal.name)
.isEquals();
}

测试仍然是绿色的。为什么会这样?

在周日早上编辑:我做了一些更多的测试,发现它甚至不是优化,但在运行此测试时甚至会导致大量调用的开销:

class AnimalSpec extends Specification {

def 'performance test of == vs equals'() {
given:
def animal = new Animal("Fluffy", 4)
when:
def doubleEqualsSignBenchmark = 'benchmark 1M invocation of == on'(animal)
def equalsMethodBenchmark = 'benchmark 1M invocation of .equals(o) on'(animal)
println "1M invocation of == took ${doubleEqualsSignBenchmark} ms and 1M invocations of .equals(o) took ${equalsMethodBenchmark}ms"
then:
doubleEqualsSignBenchmark < equalsMethodBenchmark
}

long 'benchmark 1M invocation of == on'(Animal animal) {
return benchmark {
def i = {
animal == animal
}
1.upto(1_000_000, i)
}
}

long 'benchmark 1M invocation of .equals(o) on'(Animal animal) {
return benchmark {
def i = {
animal.equals(animal)
}
1.upto(1_000_000, i)
}
}

def benchmark = { closure ->
def start = System.currentTimeMillis()
closure.call()
def now = System.currentTimeMillis()
now - start
}
}

我预计这个测试会成功,但我运行了几次,它从来没有绿色......

1M invocation of == took 164 ms and 1M invocations of .equals(o) took 139ms

Condition not satisfied:

doubleEqualsSignBenchmark < equalsMethodBenchmark
| | |
164 | 139
false

当调用次数增加到 1B 时,优化变得可见:

1B invocation of == took 50893 ms and 1B invocations of .equals(o) took 75568ms

最佳答案

这个优化存在是因为下面的表达式:

animal1 == animal1

Groovy 转换为以下方法调用:

ScriptBytecodeAdapter.compareEqual(animal1, animal1)

现在,如果我们看一下 this method's source code我们会发现,在第一步中,这个方法使用了很好的旧 Java 对象引用比较——如果表达式的两边都指向同一个引用,它只返回 true。和 equals(o)compareTo(o) (在比较实现 Comparable<T> 接口(interface)的对象的情况下)方法不会被调用:

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();

// ....
}

在你的情况下,leftright变量指向相同的对象引用,因此方法中的第一个检查匹配和 true被退回。

如果您在此位置放置一个断点(ScriptBytecodeAdapter.java 第 685 行),您将看到调试器到达该点并返回 true从该方法的第一行开始。

反编译 Groovy 代码

作为一个很好的练习,您可以看一下下面的示例。这是一个使用 Animal_script.groovy 的简单 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)) .

希望对您有所帮助。

关于java - Groovy == 运算符未达到 Java equals(o) 方法 - 这怎么可能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52555849/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com