Bỏ qua để đến nội dung

equals, hashCode và toString

Mọi class trong Java, dù không khai báo extends, đều ngầm kế thừa từ lớp Object - lớp cung cấp sẵn ba phương thức quan trọng: toString(), equals(), hashCode().

2. toString() - biểu diễn đối tượng dưới dạng chuỗi

Phần tiêu đề “2. toString() - biểu diễn đối tượng dưới dạng chuỗi”

Mặc định, toString() trả về một chuỗi không mấy hữu ích (tên lớp + mã băm dạng hex):

public class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
}
Point p = new Point(3, 4);
System.out.println(p); // Point@1b6d3586 - KHÔNG hữu ích!

Override toString() để có chuỗi biểu diễn dễ đọc, hữu ích cho debug và log:

public class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
@Override
public String toString() {
return "Point(" + x + ", " + y + ")";
}
}
Point p = new Point(3, 4);
System.out.println(p); // Point(3, 4) - dễ đọc!

3. equals() - so sánh nội dung, không phải địa chỉ

Phần tiêu đề “3. equals() - so sánh nội dung, không phải địa chỉ”

Mặc định, equals() kế thừa từ Object chỉ so sánh tham chiếu (giống hệt ==) - hai đối tượng khác nhau dù nội dung giống hệt vẫn bị coi là “không bằng nhau”:

Point p1 = new Point(3, 4);
Point p2 = new Point(3, 4);
System.out.println(p1 == p2); // false - khác đối tượng trên heap
System.out.println(p1.equals(p2)); // false - mặc định equals() giống ==, chưa override!

Override equals() để so sánh nội dung:

@Override
public boolean equals(Object obj) {
if (this == obj) return true; // cùng tham chiếu -> chắc chắn bằng
if (obj == null || getClass() != obj.getClass()) return false; // khác kiểu -> không bằng
Point other = (Point) obj;
return this.x == other.x && this.y == other.y; // so sánh từng field
}
Point p1 = new Point(3, 4);
Point p2 = new Point(3, 4);
System.out.println(p1.equals(p2)); // true - sau khi đã override đúng cách

4. hashCode() - PHẢI override cùng lúc với equals()

Phần tiêu đề “4. hashCode() - PHẢI override cùng lúc với equals()”

⚠️ Quy tắc bắt buộc của Java: hai đối tượng bằng nhau theo equals() thì phải có cùng hashCode(). Nếu chỉ override equals() mà quên hashCode(), các cấu trúc dựa trên hash như HashMap, HashSet sẽ hoạt động sai:

@Override
public int hashCode() {
return Objects.hash(x, y); // java.util.Objects cung cấp tiện ích tính hash từ nhiều field
}
import java.util.HashSet;
import java.util.Set;
Set<Point> points = new HashSet<>();
points.add(new Point(3, 4));
// Nếu KHÔNG override hashCode() (dù đã override equals()):
System.out.println(points.contains(new Point(3, 4))); // false SAI! (dù equals() trả về true)
// Sau khi override CẢ HAI equals() và hashCode() đúng cách:
System.out.println(points.contains(new Point(3, 4))); // true ĐÚNG

Hầu hết IDE (IntelliJ IDEA, VS Code) có chức năng tự sinh equals(), hashCode(), toString() dựa trên các field bạn chọn - không cần viết tay, giảm nguy cơ viết sai quy tắc.

  • toString(): nên override để có chuỗi biểu diễn dễ đọc, hữu ích khi debug/log
  • equals(): nên override để so sánh nội dung thay vì mặc định so sánh tham chiếu
  • hashCode(): bắt buộc override cùng lúc với equals() - nếu không, HashMap/HashSet sẽ hoạt động sai
  • Có thể dùng Objects.hash(...) và để IDE tự sinh code cho ba phương thức này