Java 深度比较 Returns 比较深度副本时为假
Java Deep Comparison Returns False when Comparing a Deep Copy
我创建了一个抽象的 class Fruit,它覆盖了 equals() 方法。然后我创建了一个subclass,Orange,它覆盖了copy()和equals()方法。在我的测试文件 TestFruit.java 中,我正在创建一个橙子数组并测试它们的方法。我正在尝试创建橙色的深度副本,并在父橙色和副本之间进行深度比较。但是,在我的输出中,比较总是 returns false。我检查了父项和副本的属性,它们似乎是一样的。任何指针将不胜感激。我对 Java 和复制还很陌生。我在下面附上了我的代码。
Fruit.java:
package juicer;
import copy.Copyable;
public abstract class Fruit implements Copyable, Cloneable
{
private double mass;
private boolean isJuicedRemoved;
protected Fruit(double theMass)
throws IllegalMassException
{
{
if (theMass <= 0)
{
throw new IllegalMassException(theMass);
}
else
{
this.mass = theMass;
this.isJuicedRemoved = false;
}
}
}
protected Fruit(Fruit fruit)
{
this.mass = fruit.mass;
this.isJuicedRemoved = fruit.isJuicedRemoved;
}
public double getMass()
{
return mass;
}
public boolean getIsJuicedExtracted()
{
return isJuicedRemoved;
}
protected void setMass(double value)
{
this.mass = value;
}
protected abstract double juiceRatio();
public double extractJuice()
{
double liquidMass = amountJuice();
if (!isJuicedRemoved)
{
isJuicedRemoved = true;
mass -= liquidMass;
}
return liquidMass;
}
public double amountJuice()
{
if (isJuicedRemoved) return 0.0;
return mass * juiceRatio();
}
@Override
public boolean equals(Object obj)
{
// Steps to override the equals() method():
// Step 1: Test if obj is an instance of Fruit.
// If it is not, then return false.
if (!(obj instanceof Fruit)) return false;
// Step 2: Cast obj to an Fruit.
Fruit rhs = (Fruit)obj;
// Step 3: Test if the data fields of the invoking object are
// equal to the ones in rhs using a deep comparison
// and return this result.
return super.equals(obj) && // test for equality in the super class
mass == rhs.mass &&
isJuicedRemoved == rhs.isJuicedRemoved;
}
@Override
public int hashCode()
{
int result = super.hashCode();
result = 31*result + Double.hashCode(mass);
result = 31*result + Boolean.hashCode(isJuicedRemoved);
return result;
}
@Override
public Object clone() throws CloneNotSupportedException
{
Fruit objectClone = (Fruit)super.clone();
objectClone.mass = mass;
objectClone.isJuicedRemoved = isJuicedRemoved;
return objectClone;
}
@Override
public String toString()
{
return "\tmass = " + mass +
"\n\tisJuiceExtracted = " + isJuicedRemoved + "\n";
}
}
Orange.java:
package juicer;
public class Orange extends Fruit
{
public Orange(double mass)
{
super(mass);
}
// copy constructor
public Orange(Orange other)
{
super(other);
}
@Override
protected double juiceRatio()
{
return 0.87;
}
@Override
public boolean equals(Object obj)
{
// Steps to override the equals() method():
// Step 1: Test if obj is an instance of Orange.
// If it is not, then return false.
if (!(obj instanceof Orange)) return false;
// Step 2: Cast obj to an Orange.
// This step is not needed since the only data fields this
// class has are the ones it inherits.
// Step 3: Test if the data fields of the invoking object are
// equal to the ones in rhs using a deep comparison
// and return this result.
return super.equals(obj);
}
@Override
public Object copy()
{
return new Orange(this);
}
@Override
public String toString()
{
return "Orange:\n" + super.toString();
}
}
TestFruit.java:
package test;
import juicer.*;
import java.util.Random;
public class TestFruit
{
public static void main(String[] args)
{
Orange[] oranges = new Orange[1];
//Random double generator for mass
Random rd = new Random();
//create oranges
for (int i = 0; i <= oranges.length - 1; i++ )
{
oranges[i] = new Orange(rd.nextDouble());
}
for (Orange orange : oranges)
{
Orange orangeCopy = new Orange(orange);
if (orange == orangeCopy)
{
System.out.print("The comparison is true!");
}
else
{
System.out.print("Does not match.");
}
}
}
}
Java 中的一个常见误解是 ==
与 .equals()
的使用。当您使用 ==
比较 Java 中的两个对象时,它在内部比较其内存地址。 ==
不是 实际上调用 .equals()
.
在这种情况下,您有两个不同的橙色对象,因此比较总是 return false。
如果您使用 a.equals(b)
,那么它实际上会调用您实现的 equals
方法。
正如@Andreas 在评论中指出的那样,还有另一个问题。在Fruit
中调用super.equals(obj)
会调用equals
的超类实现,而Fruit
的超类是Object
。 Object.equals()
的行为与 ==
相同(即也检查引用相等性)。覆盖 .equals()
的是 not trivial,因此让 IDE 为您生成它通常会很好。
与 C++ 等语言相比,Java 没有运算符重载。这意味着您不能为 ==
定义不同的实现。这就是为什么在比较任何非原始类型时最好总是调用 .equals()
的原因(除非您显式检查引用相等性,这种情况很少见)。
我创建了一个抽象的 class Fruit,它覆盖了 equals() 方法。然后我创建了一个subclass,Orange,它覆盖了copy()和equals()方法。在我的测试文件 TestFruit.java 中,我正在创建一个橙子数组并测试它们的方法。我正在尝试创建橙色的深度副本,并在父橙色和副本之间进行深度比较。但是,在我的输出中,比较总是 returns false。我检查了父项和副本的属性,它们似乎是一样的。任何指针将不胜感激。我对 Java 和复制还很陌生。我在下面附上了我的代码。
Fruit.java:
package juicer;
import copy.Copyable;
public abstract class Fruit implements Copyable, Cloneable
{
private double mass;
private boolean isJuicedRemoved;
protected Fruit(double theMass)
throws IllegalMassException
{
{
if (theMass <= 0)
{
throw new IllegalMassException(theMass);
}
else
{
this.mass = theMass;
this.isJuicedRemoved = false;
}
}
}
protected Fruit(Fruit fruit)
{
this.mass = fruit.mass;
this.isJuicedRemoved = fruit.isJuicedRemoved;
}
public double getMass()
{
return mass;
}
public boolean getIsJuicedExtracted()
{
return isJuicedRemoved;
}
protected void setMass(double value)
{
this.mass = value;
}
protected abstract double juiceRatio();
public double extractJuice()
{
double liquidMass = amountJuice();
if (!isJuicedRemoved)
{
isJuicedRemoved = true;
mass -= liquidMass;
}
return liquidMass;
}
public double amountJuice()
{
if (isJuicedRemoved) return 0.0;
return mass * juiceRatio();
}
@Override
public boolean equals(Object obj)
{
// Steps to override the equals() method():
// Step 1: Test if obj is an instance of Fruit.
// If it is not, then return false.
if (!(obj instanceof Fruit)) return false;
// Step 2: Cast obj to an Fruit.
Fruit rhs = (Fruit)obj;
// Step 3: Test if the data fields of the invoking object are
// equal to the ones in rhs using a deep comparison
// and return this result.
return super.equals(obj) && // test for equality in the super class
mass == rhs.mass &&
isJuicedRemoved == rhs.isJuicedRemoved;
}
@Override
public int hashCode()
{
int result = super.hashCode();
result = 31*result + Double.hashCode(mass);
result = 31*result + Boolean.hashCode(isJuicedRemoved);
return result;
}
@Override
public Object clone() throws CloneNotSupportedException
{
Fruit objectClone = (Fruit)super.clone();
objectClone.mass = mass;
objectClone.isJuicedRemoved = isJuicedRemoved;
return objectClone;
}
@Override
public String toString()
{
return "\tmass = " + mass +
"\n\tisJuiceExtracted = " + isJuicedRemoved + "\n";
}
}
Orange.java:
package juicer;
public class Orange extends Fruit
{
public Orange(double mass)
{
super(mass);
}
// copy constructor
public Orange(Orange other)
{
super(other);
}
@Override
protected double juiceRatio()
{
return 0.87;
}
@Override
public boolean equals(Object obj)
{
// Steps to override the equals() method():
// Step 1: Test if obj is an instance of Orange.
// If it is not, then return false.
if (!(obj instanceof Orange)) return false;
// Step 2: Cast obj to an Orange.
// This step is not needed since the only data fields this
// class has are the ones it inherits.
// Step 3: Test if the data fields of the invoking object are
// equal to the ones in rhs using a deep comparison
// and return this result.
return super.equals(obj);
}
@Override
public Object copy()
{
return new Orange(this);
}
@Override
public String toString()
{
return "Orange:\n" + super.toString();
}
}
TestFruit.java:
package test;
import juicer.*;
import java.util.Random;
public class TestFruit
{
public static void main(String[] args)
{
Orange[] oranges = new Orange[1];
//Random double generator for mass
Random rd = new Random();
//create oranges
for (int i = 0; i <= oranges.length - 1; i++ )
{
oranges[i] = new Orange(rd.nextDouble());
}
for (Orange orange : oranges)
{
Orange orangeCopy = new Orange(orange);
if (orange == orangeCopy)
{
System.out.print("The comparison is true!");
}
else
{
System.out.print("Does not match.");
}
}
}
}
Java 中的一个常见误解是 ==
与 .equals()
的使用。当您使用 ==
比较 Java 中的两个对象时,它在内部比较其内存地址。 ==
不是 实际上调用 .equals()
.
在这种情况下,您有两个不同的橙色对象,因此比较总是 return false。
如果您使用 a.equals(b)
,那么它实际上会调用您实现的 equals
方法。
正如@Andreas 在评论中指出的那样,还有另一个问题。在Fruit
中调用super.equals(obj)
会调用equals
的超类实现,而Fruit
的超类是Object
。 Object.equals()
的行为与 ==
相同(即也检查引用相等性)。覆盖 .equals()
的是 not trivial,因此让 IDE 为您生成它通常会很好。
与 C++ 等语言相比,Java 没有运算符重载。这意味着您不能为 ==
定义不同的实现。这就是为什么在比较任何非原始类型时最好总是调用 .equals()
的原因(除非您显式检查引用相等性,这种情况很少见)。