3.Định nghĩa phương thức equals với các trường(field) có thể sữa đổi

Ta xây dựng lại lớp Point như sau:

public class Point { 

    private int x;
    private int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
 
    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void setX(int x) { // Problematic
        this.x = x;
    }
 
    public void setY(int y) {
        this.y = y;
    }

    @Override public boolean equals(Object other) {
        boolean result = false;
        if (other instanceof Point) {
            Point that = (Point) other;
            result = (this.getX() == that.getX() && this.getY() == that.getY());
        }
        return result;
    }

    @Override public int hashCode() {
        return (41 * (41 + getX()) + getY());
    }
}

Lớp Point được xây dựng lại có sự khác nhau so với ban đầu: bỏ từ khóa final khi khai báo 2 trường x và y, các phương thức equals và hashCode được định nghĩa theo điều khoản các trường của chúng có thể thay đổi giá trị, vì vậy kết quả của chúng thay đổi khi thay đổi giá trị của các trường. Điều này có thể gây ra những hiệu ứng kỳ lạ một khi bạn đặt points trong collection:

Point p = new Point(1, 2);

HashSet<Point> coll = new HashSet<Point>();
coll.add(p);

System.out.println(coll.contains(p)); // prints true

Nhưng bây giờ, bạn thay đổi giá trị một trường trong p:

p.setX(p.getX() + 1);

System.out.println(coll.contains(p)); // prints false 

Điều này trông rất kỳ lạ, p đã ở đâu? Kết quả lạ hơn nếu bạn kiểm tra xem Iterator của tập chứa p:

Iterator<Point> it = coll.iterator();
boolean containedP = false;
while (it.hasNext()) {
    Point nextP = it.next();
    if (nextP.equals(p)) {
        containedP = true;
        break;
    }
}

System.out.println(containedP); // prints true

Điều gì xảy ra, tất nhiên, là sau khi thay đổi trường x, điểm p có hash bucket nằm ngoài tập coll. Đó là, bản gốc hash bucket của coll không còn tương ứng với giá trị mới hash bucket của p.
Bài học rút ra từ ví dụ này là khi phương thức equals và hashCode phụ thuộc vào trạng thái có thể thay đổi, nó có thể gây ra nhiều vấn đề cho người dùng. Khi bạn put các đối tượng này vào collecition, bạn cần lưu ý là không sửa đổi các trạng thái phụ thuộc này.

Tóm lại, nếu bạn put một đối tượng tạo ra từ lớp mà bạn xây dựng vào collection, bạn không nên override 2 phương thức equals và hashCode, để lớp mà bạn xây dựng kế thừa 2 phương thức equals và hashCode mặc định từ lớp Object. Trong trường hợp cần so sánh các giá trị trong lớp của bạn, thì bạn nên đặt một tên khác equals, chẳng hạn equalsContent. Ngược lại, bạn phải sử dụng các giá trị không thể sữa đổi trong phương thức equals và hashCode.

Bạn thấy bài viết này thế nào?

Các bài viết liên quan:
Kỹ thuật lập trình hướng đối tượng – Phần 3