07 tháng 7 2006

Equals method

Mọi class trong Java đều kế thừa class Object, vì thế nó kế thừa luôn method equals của class Object. Khốn khổ thay, method equals mặc định của class Object chỉ đơn giản là toán tử ==, nó chỉ đơn thuần so sánh liệu 2 object đó có cùng tham chiếu tới một đối tượng không mà thôi chứ không phái là so sánh giá trị của 2 object đó.

Ví dụ bây giờ mình có một class mô tả một điểm trong mặt phẳng như sau

Đoạn code sau sẽ luôn trả về false

Bởi vì p1 và p2 tham chiếu tới 2 đối tượng khác nhau trong bộ nhớ nên kết quả của p1.equals(p2) luôn luôn là false cho dù ta thấy chúng có cùng miêu tả điểm (1,1). Đó không phải là điều mà chúng ta mong muốn, phương thức equals phải trả về true trong trường hợp này. Muốn thế, ta phải override phương thức equals, bắt nó tuân theo ý muốn của ta. Bạn có thể thấy là viết phương thức equals thực hiện đúng yêu cầu này thật đơn giản, hai điểm giống nhau khi có cùng tọa độ x và tọa độ y.

Tuy nhiên, cuộc đời không phải lúc nào cũng đơn giản như vậy :D. Thử tưởng tượng một ngày xấu trời nào đó p2 không phái là tham chiếu tới đối tượng thuộc lớp Point hay p2 là null thì sẽ ra sao nhỉ? Tất nhiên rồi, error chứ sao, Java sẽ ném cho bạn một đống exception ngay. Java Language Specification đòi hỏi phương thức equals mà bạn viết phải thõa mãn những yêu cầu sau:


  • Nếu p không phải là tham chiếu null thì p.equals(p) phải trả về true.
  • Có tính đối xứng: với p1, p2 bất kỳ không phải null, p1.equals(p2) trả về true khi và chỉ khi p2.equals(p1) trả về true.
  • Có tính bắc cầu: với p1, p2, p3 bất kỳ không phải null,nếu p1.equals(p2)p2.equals(p3) đều trả về true thì p1.equals(p3) bắt buộc cũng phải trả về true.
  • Cố định, không thay đổi: nếu đối tượng mà p1, p2 tham chiếu tới không bị thay đổi thì việc gọi lặp lại lệnh p1.equals(p2) trả về cùng một giá trị.
  • Với p bất kỳ khác null, p.equals(null) phải trả về false


Lúc này thì phương thức equals có lẽ sẽ được bạn sửa lại thành:

Chú ý là ở đây bạn không cần phải check xem other có phải là null không bởi vì nếu other là null thì biểu thức other instanceof Point sẽ trả về false luôn cho bạn rồi. Phương thức equals trong class Integer của Java và nhiều class khác cũng được viết tương tự vậy:

Nhưng, một lần nữa, cuộc đời là không đơn giản chút nào và Java cũng vậy :D
Giả sử bây giờ bạn có một lớp mới là ColorPoint kế thừa lớp Point và bạn cũng tạo cho nó một constructor và một phương thức equals

Bây giờ bạn nghĩ đoạn code sau sẽ cho ra kết quả như thế nào

Theo yêu cầu về tính đối xứng, p.equals(cp)cp.equals(p) phải trả về cùng một giá trị. Tuy nhiên, ở đây, p.equals(cp) sẽ trả về true còn cp.equals(p) sẽ trả về false. Nguyên nhân là nằm ở toán tử instanceof, ColorPoint là class con của class Point nên cp instanceof Pointtrue còn p instanceof ColorPointfalse. Phương thức equals của chúng ta đã không đạt yêu cầu đề ra. Tới đây, bạn có thể thắc mắc là vậy phương thức equals trong class Integer của Java cũng không đạt hay sao, chả lẽ Sun kém vậy hay sao :D? Không phải như vậy. Có một sự khác nhau rất lớn giữa class Point của chúng ta và class Integer, class Integer là final, không thể tạo một class khác extends class Integer được nên lỗi trên sẽ không bao giờ xảy ra và phương thức equals luôn luôn hoạt động tốt.

Cách tốt nhất cho phương thức equals của chúng ta là sử dụng phương thức getClass() thay cho sử dụng instanceof trong việc kiểm tra xem có đúng đối tượng thuộc đúng loại class mà ta định ép kiểu để so sánh hay không.

Và cho class ColorPoint là

Lúc này thì cả p.equals(cp)cp.equals(p) đều trả về false, một điểm có màu thì khác một điểm không có màu chứ nhỉ?

0 Comments:

Post a Comment




 

Copyright 2006| Blogger Templates by GeckoandFly modified and converted to Blogger Beta by Blogcrowds.
No part of the content or the blog may be reproduced without prior written permission.