我今天遇到了一个有趣的(非常令人沮丧的) equals()方法的问题,导致我认为是经过良好测试的类崩溃并导致一个错误很长一段时间追查。

为了完整起见,我没有使用IDE或调试器 - 只是老式的文本编辑器和System.out。时间非常有限,这是一个学校项目。

无论如何 -

我正在开发一个基本的购物车,其中可能包含 Book 对象的 ArrayList 。为了实现Cart的 addBook() removeBook() hasBook()方法,我想检查一下<代码> Book 已经存在于 Cart 中。我走了 -

public boolean equals(Book b) {
    ... // More code here - null checks
    if (b.getID() == this.getID()) return true;
    else return false;
}

一切都在测试中运行良好。我创建了6个对象并用数据填充它们。在 Cart 上做了很多添加,删除,has()操作,一切正常。我读过你可以拥有 equals(TYPE var) equals(Object o){(CAST)var} 但是我认为工作,这没关系太多。

然后我遇到了一个问题 - 我需要在Book类中创建一个 Book 对象,其中 ID 。不会输入任何其他数据。基本上如下:

public boolean hasBook(int i) {
    Book b = new Book(i);
    return hasBook(b);
}

public boolean hasBook(Book b) {
    // .. more code here
    return this.books.contains(b);
}

突然间, equals(Book b)方法不再有效。这花了很长时间没有一个好的调试器跟踪,并假设 Cart 类已经过适当的测试和纠正。将 equals()方法交换到以下内容后:

public boolean equals(Object o) {
    Book b = (Book) o;
    ... // The rest goes here   
}

一切都开始了。是否有理由该方法决定不采用Book参数,即使它 Book 对象?唯一的区别似乎是它是在同一个类中实例化的,并且只填充了一个数据成员。我很困惑。请揭开一些亮点?

有帮助吗?

解决方案

在Java中,从 Object 继承的 equals()方法是:

public boolean equals(Object other);

换句话说,参数必须是 Object 类型。

ArrayList 使用正确的equals方法,在那里你总是调用那个没有正确覆盖 Object 的等于的方法。

不正确地覆盖方法会导致问题。

我每次都覆盖等于以下内容:

@Override
public boolean equals(Object other){
    if (other == null) return false;
    if (other == this) return true;
    if (!(other instanceof MyClass))return false;
    MyClass otherMyClass = (MyClass)other;
    ...test other properties here...
}

使用 @Override 注释可以帮助减轻愚蠢的错误。

只要您认为自己覆盖了超级类或接口的方法,就可以使用它。这样,如果你做错了,你就会遇到编译错误。

其他提示

如果您使用eclipse,请转到顶部菜单

  

来源 - &gt;生成equals()和   hashCode()方法

对你的问题稍微偏离主题,但无论如何它可能值得一提:

Commons Lang 有一些很好的方法可以用来覆盖equals和hashcode。查看 EqualsBuilder.reflectionEquals(...) HashCodeBuilder.reflectionHashCode(...)。过去让我感到很头疼 - 当然,如果你只是想做“等于”在ID上它可能不适合您的情况。

我同意你在覆盖equals(或任何其他方法)时应该使用 @Override 注释。

保存样板代码的另一个快速解决方案是 Lombok EqualsAndHashCode注释。它简单,优雅,可定制。并且不依赖于IDE 。例如;

import lombok.EqualsAndHashCode;

@EqualsAndHashCode(of={"errorNumber","messageCode"}) // Will only use this fields to generate equals.
public class ErrorMessage{

    private long        errorNumber;
    private int         numberOfParameters;
    private Level       loggingLevel;
    private String      messageCode;

请参阅选项,以自定义在equals中使用哪些字段。 Lombok在 maven 中可用。只需使用提供的范围添加它:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.14.8</version>
    <scope>provided</scope>
</dependency>
Android Studio中的

是 alt + insert ---&gt; equals和hashCode

示例:

    @Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Proveedor proveedor = (Proveedor) o;

    return getId() == proveedor.getId();

}

@Override
public int hashCode() {
    return getId();
}

考虑:

Object obj = new Book();
obj.equals("hi");
// Oh noes! What happens now? Can't call it with a String that isn't a Book...

instanceOf 语句通常用于实现equals。

这是一个很受欢迎的陷阱!

问题是使用 instanceOf 违反了对称规则:

(object1.equals(object2)== true) 当且仅当 (object2.equals(object1))

如果第一个equals为true,则object2是子类的实例 obj1所属的类,那么第二个等于将返回false!

如果ob1所属的被认定的类被声明为final,那么这个 问题不会出现,但一般来说,你应该测试如下:

this.getClass()!= otherObject.getClass(); 如果没有,返回false,否则测试 要比较相等的字段!

recordId是对象的属性

@Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Nai_record other = (Nai_record) obj;
        if (recordId == null) {
            if (other.recordId != null)
                return false;
        } else if (!recordId.equals(other.recordId))
            return false;
        return true;
    }
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top