equals, hashCode và toString
1. Mọi class đều kế thừa từ Object
Phần tiêu đề “1. Mọi class đều kế thừa từ Object”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 heapSystem.out.println(p1.equals(p2)); // false - mặc định equals() giống ==, chưa override!Override equals() để so sánh nội dung:
@Overridepublic 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ách4. 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:
@Overridepublic 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 ĐÚNG5. IDE có thể tự sinh code này
Phần tiêu đề “5. IDE có thể tự sinh code này”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.
Tổng kết
Phần tiêu đề “Tổng kết”toString(): nên override để có chuỗi biểu diễn dễ đọc, hữu ích khi debug/logequals(): nên override để so sánh nội dung thay vì mặc định so sánh tham chiếuhashCode(): bắt buộc override cùng lúc vớiequals()- nếu không,HashMap/HashSetsẽ hoạt động sai- Có thể dùng
Objects.hash(...)và để IDE tự sinh code cho ba phương thức này