当我有这样的课程时
public class MyClass {
private ClassA classA;
private ClassB classB;
}
我的equals和hascode函数经常会像这样混乱
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((classB == null) ? 0 : classB.hashCode());
result = prime * result + ((classA == null) ? 0 : classA.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyClass other = (MyClass) obj;
if (classB == null) {
if (other.classB != null)
return false;
} else if (!classB.equals(other.classB))
return false;
if (classA == null) {
if (other.classA != null)
return false;
} else if (!classA.equals(other.classA))
return false;
return true;
}
所以我开始忽略有人会将苹果与香蕉进行比较。我的应用程序中的HashSets或HashMaps总是包含相同的对象类型...我从未创建过List并开始将Integer与String混合。
@Override
public int hashCode() {
return classB.hashCode();
}
@Override
public boolean equals(Object obj) {
MyClass other = (MyClass) obj;
return other.classB.equals(this.classB) && other.classA.equals(this.classA);
}
忽略不常见的情况并抛出异常是不好或好的做法?我认为在比较完整的不同类时,这通常是一个错误。
答案 0 :(得分:7)
让Array.from()
或var days = [...Array((new Date).getDate())].map((e, i) => i + 1)
console.log(days)
抛出异常是非常糟糕的做法(我建议您阅读Effective Java了解详细信息)。
此外,您的方法也不必要地复杂。从Java 7开始,这几乎就是编写这些方法的规范方法:
equals()
答案 1 :(得分:-2)
如何缩短equals()
方法(hashCode
方法忽视classA
)是不好的做法,但可能存在适当的用例)。让我用equals
来解释这个问题。
您明显的做法是假设所有Objects
都是MyClass
类型。但Java是一种类型安全的语言,因此您应始终定义所需的数据类型。否则,您可以简单地在任何地方预期Objects
并按照您的示例中的方式投射它们。这可能会像脚本语言(例如,Javascript)一样工作,但是一旦你的代码库变得更大并且多个开发人员试图理解和改进你的代码,它就会变得混乱并且你失去了类型安全的好处(例如,在编译期间看到与类型相关的错误。)
您在这里面临的另一个问题是您正在使用Object
的界面。但是,假设您使用正确的数据类型定义了一个额外的(重载)方法,那么您的代码将如下所示:
public boolean equals(MyClass other) {
return other.classB.equals(this.classB) && other.classA.equals(this.classA);
}
现在你有一种方法可以完全符合你的预期,对吧?不完全:
MyClass myClass1 = new MyClass();
MyClass myClass2 = clone(myClass1)
myClass1.equals(myClass2) // works => true
myClass2.equals(myClass1) // the same
Object myClass3 = (Object) myClass2
myClass3.equals(myClass1) // Compares the address with the default Object.equals() => false
myClass1.equals(myClass3) // the same
您看到,只是更改数据类型,结果会发生变化,但myClass1
,myClass2
和myClass3
非常相同。您不会收到异常或编译错误。好吧,让我们通过实际覆盖方法来改变它(正如你所做的那样)。
@Override
public boolean equals(Object obj) {
MyClass other = (MyClass) obj;
return this.equals(other); // this is the overloaded method
}
现在,如果ClassCastException
不是obj
类型,则会获得MyClass
。只要您知道如何实现它,这就有效,因为签名中没有Throwable
。还需要考虑的另一件事是ClassCastException
扩展RuntimeException
,这对开发人员来说是意料之外的(即未经检查)。这意味着如果我不知道它是如何实现的(例如,因为它是编译库的一部分),或者即使你是在几年后继续开发并且不记得它的人,也可能是拿出这样的东西:
public class MyExtraClass extends MyClass {
private String someIrrelevantStuff;
// I don't want or need a separate equals
// and assume that MyClass or Object takes care of it
}
MyClass myClass1 = new MyClass();
MyClass myClass2 = (MyClass) new MyExtraClass();
myClass2.equals(myClass1) // works => false
myClass1.equals(myClass2) // Throws ClassCastException
// Application crashes completely at this point
// just because I didn't expect the unchecked exception
我想说,这个例子(即子类)不是 uncommon 或错误,但它失败了。如果您在签名中添加了例外,它将再次超载,并且您将返回到您开始的位置。你看,由于Object
的界面,没有任何出路。
但是,在当前版本中询问此接口是否仍适用于Java是合理的。提供默认的deepEquals()
方法而不是当前的 equalsAddressOf()是不合适的(不要与当前适用于数组的deepEquals()
混淆)? Scala已经用其case classes
解决了这个问题。如果您想要更短的代码,可以考虑切换到Scala或(可选)不安全的语言,如Python。
毕竟,如果你真的喜欢区分false
状态。您可以简单地使用自己的界面:
public abstract class DeeplyComparableObject extends Object {
public abstract boolean deeplyEquals(Object o) throws TypeNotEqualException;
}
public class TypeNotEqualException extends Exception {
}