בפוסט זה אתאר את ההבנה שלי בשיטות עקיפה
Apache Commons מספק שני שיעורי עוזר נהדרים לשיטות קריאה
hashCode()
ו equals()
. אני רוצה לדבר על יישום ברירת המחדל שלהם, כמו גם איך לעקוף אותם בצורה נכונה. אכתוב גם על הטמעת שיטות אלו באמצעות מחלקות העזר של חבילת Apache Common. תוכן הפוסט הזה:
- באמצעות
hashCode()
וequals()
. - עוקף את התנהגות ברירת המחדל.
- עקיפה
hashCode()
ושימושequals()
ב- Apache Commons Lang. - משהו שחשוב לזכור.
- תשומת לב מיוחדת בעת שימוש ב-ORM.
hashCode()
ו equals()
הוגדרו במחלקה Object
, שהיא מחלקת האב עבור אובייקטי Java. לכן, כל אובייקטי Java יורשים את מימוש ברירת המחדל משיטות אלה.
שימוש ב-hashCode() ושווה()
השיטהhashCode()
משמשת לקבלת מספר שלם ייחודי עבור אובייקט נתון. כאשר יש לאחסן אובייקט כמבנה נתונים בטבלת hash (נקראת גם bucket), מספר זה משמש לקביעת מיקומו בטבלה זו. כברירת מחדל, השיטה hashCode()
עבור אובייקט מחזירה את המספר של מיקום הזיכרון שבו האובייקט מאוחסן. השיטה equals()
, כפי ששמה מרמז, משמשת פשוט כדי לבדוק את השוויון של שני אובייקטים. יישום ברירת המחדל של שיטה זו פשוט בודק את ההפניות של שני אובייקטים כדי לראות אם הם שווים.
עוקף התנהגות ברירת מחדל
הכל עובד בסדר כל עוד אתה לא מעקף אף אחת מהשיטות האלה בשיעורים שלך. אבל לפעמים יישומים צריכים לשנות את התנהגות ברירת המחדל של אובייקטים מסוימים. ניקח דוגמה שבה יש לךEmployee
. בוא נכתוב את המבנה המינימלי האפשרי של מחלקה כזו.
public class Employee
{
private Integer id;
private String firstname;
private String lastName;
private String department;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
}
למחלקה המתוארת למעלה Employee
יש כמה תכונות בסיסיות ושיטות עזר. כעת נסתכל על מצב פשוט שבו עלינו להשוות בין שני אובייקטים של המחלקה Employee
.
public class EqualsTest {
public static void main(String[] args) {
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
//Печатает false в консоли
System.out.println(e1.equals(e2));
}
}
לא צריך ראיית רוח כדי לנחש שהשיטה שנכתבה למעלה תחזיר "שקר". אבל האם זה באמת נכון, בהתחשב בכך ששני האובייקטים הללו זהים? ביישום בזמן אמת, השיטה חייבת להחזיר אמת. כדי להשיג את ההתנהגות הנכונה, עלינו לעקוף את השיטה equals()
, כפי שנעשה להלן:
public boolean equals(Object o) {
if(o == null)
{
return false;
}
if (o == this)
{
return true;
}
if (getClass() != o.getClass())
{
return false;
}
Employee e = (Employee) o;
return (this.getId() == e.getId());
}
הוסף שיטה זו לכיתה שלך Employee
ובדיקת השוויון תחזיר "נכון". עם זאת, האם עשינו הכל? עדיין לא. בואו נבדוק את הכיתה המותאמת שלנו בדרך אחת נוספת.
import java.util.HashSet;
import java.util.Set;
public class EqualsTest
{
public static void main(String[] args)
{
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
//Печатает 'true'
System.out.println(e1.equals(e2));
Set employees = new HashSet();
employees.add(e1);
employees.add(e2);
//Печатает два an object
System.out.println(employees);
}
}
הפקודה System.out.println(employee)
מדפיסה שני אובייקטים. אם שני האובייקטים היו שווים, ורק Set
אובייקטים ייחודיים היו כלולים, אז HashSet
צריך להיות רק מופע אחד בפנים, כלומר. שני האובייקטים מתייחסים לאותם מופעים של המחלקה Employee
. מה פספסנו? פספסנו את השיטה החשובה השנייה hashCode()
. כפי שאומר התיעוד של Java, אם אתה עוקף את השיטה equals()
, אתה נדרש לעקוף את השיטה hashCode()
. אז בואו נוסיף שיטה נוספת לכיתה שלנו Employee
.
@Override
public int hashCode()
{
final int PRIME = 31;
int result = 1;
result = PRIME * result + getId();
return result;
}
הוספנו שיטה זו פעם אחת למחלקה שלנו, ורק אובייקט אחד יודפס, וכך, בדיקת השקילות של e1 ו-e2 הראתה אמת.
עקיפה hashCode()
ושימוש equals()
ב- Apache Commons Lang
Apache Commons מספק שני שיעורי עוזר נהדרים לשיטות קריאה hashCode()
ו- equals()
. להלן אנו רואים את השימוש:
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Employee
{
private Integer id;
private String firstname;
private String lastName;
private String department;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
@Override
public int hashCode()
{
final int PRIME = 31;
return new HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME).
toHashCode();
}
@Override
public boolean equals(Object o) {
if (o == null)
return false;
if (o == this)
return true;
if (o.getClass() != getClass())
return false;
Employee e = (Employee) o;
return new EqualsBuilder().
append(getId(), e.getId()).
isEquals();
}
}
מצד שני, אם אתה משתמש באחד מעורכי הקוד, הם אמורים גם להיות מסוגלים לקרוא כמה מבנים נחמדים עבורך. לדוגמה, אם ב-Eclipse IDE תלחץ לחיצה ימנית על class >> source > Generating hashCode() ו-equals() ... זה יפיק עבורך יישום נחמד מאוד. משהו שחשוב לזכור.
- השתמש תמיד באותן תכונות אובייקט כדי לקרוא גם וגם
hashCode()
וגםequals()
. רק במקרה שלנו, השתמשנו בemployee id
. - השיטה
equals()
חייבת להיות מתמשכת (אם האובייקט לא השתנה, השיטה חייבת להחזיר את אותו הערך). - בכל פעם
a.equals(b)
, אזa.hashCode()
חייב להיות זהה לb.hashCode()
. - אם תעקוף שיטה אחת, עליך לעקוף את השנייה.
תשומת לב מיוחדת בעת שימוש ב-ORM
אם אתה מתמודד עם ORM (ru.wikipedia.org/wiki/ORM), השתמש תמיד ב-Gutters ולעולם אל תשתמש בהפניות לשדהhashCode()
. equals()
הסיבה לכך היא שב-ORM, מעת לעת שדות נטענים באמצעות עומס עצלן ואינם נגישים עד לקריאת הגטרים שלהם. לדוגמה, בכיתה שלנו Employee
, אנו משתמשים ב e1.id == e2.id
. זה בהחלט אפשרי ששדות i
d נטענים באמצעות טעינה עצלה. אחד מהשדות עשוי להיות 0 או null ונקבל התנהגות שגויה. אבל, אם נעשה שימוש ב- e1.getId() == e2.getId()
, אנחנו יכולים להיות בטוחים גם אם השדות נטענו באמצעות טעינה עצלה; קריאה ל-getter תאכלס את השדה תחילה. זה כל מה שאני יודע על השיטות hashCode()
והשיטות equals()
. מקווה שזה יעזור למישהו איפשהו. בהצלחה עם הלימודים שלך!! נ.ב. זה הניסיון הראשון שלי לתרגום. ניסיתי להעביר הכל כמה שיותר קרוב למה שהסופר רצה לומר. אם יש לכם הערות, אנא כתבו בתגובות. אל תשפוט בקפדנות :-))) מאמר מקורי
GO TO FULL VERSION