物件導向語言JAVA中,所有物件皆自動繼承名為『Object』的物件,包含了下例數種基本函數:


這些函數為所有JAVA物件中的共同函數,在撰寫程式時,常需覆寫(Override)其中某些函數,下列將簡單說明何種情況下,需覆寫物件中toString、equals和hashCode函數。

● toString的使用時機

假設物件Foo簡單地定義如下:

class Foo {
    public final String name;
    public final int age;

    public Foo(String name,int age) {
       this.name = name;
       this.age = age;
    }
}

若宣告foo1並印出:

Foo foo1 = new Foo("A",10);
System.out.println(foo1);

可能會得到下列的輸出結果:

Foo@1a7bf11

若希望輸出的資訊有意義,可覆寫toString函數,如:

class Foo {
    public final String name;
    public final int age;

    public Foo(String name,int age) {
       this.name = name;
       this.age = age;
    }

    public String toString() {
      return name + " " + age;
    }
}

印出foo1會得到:

A 10

在未覆寫toString的情況下,呼叫println印出的foo資訊為JVM所給定此物件的識別碼,此識別碼對於程式人員並無幫助,而覆寫後,印出的物件資訊將由覆寫內容之程式碼決定,可增進除錯(Debug)時的效率。

● equals的使用時機

equals用於比較兩個物件是否相同,在這裡所謂的"equals"意義為內容上的相同,以上述的Foo物件舉例,若宣告並印出比較結果如下:

Foo foo1 = new Foo("A",10);
Foo foo2  = new Foo("A",10);
Foo foo3  = foo1;

System.out.println(foo1.equals(foo2) + " " + foo1.equals(foo3));

會印出

false true

Foo的equals函式未覆寫時,預設的equals函式執行動作,將比較兩個變數所在的記憶體位置是否相同,並未比對其實際內容,因此,將Foo中的equals函數覆寫,如:

class Foo {
    public final String name;
    public final int age;

    public Foo(String name,int age) {
       this.name = name;
       this.age = age;
    }

    public String toString() {
      return name + " " + age;
    }

    public boolean equals(Object obj) {
      if (obj != null && obj instanceof Foo)
        if (this.name != null && this.name.equals( ( (Foo) obj).name))
          if (this.age == ( (Foo) obj).age)
            return true;
 
      return false;
    }

}

equals函數標準的覆寫方法為public boolean equals(Object obj),引數obj型態為Object,所以在equals內,需檢查obj的型態是否為Foo,並比對兩物件之name和age,其中因name的型態為String,比對時需使用String物件已覆寫之equals函式進行檢查,確保this.name和obj.name的內容一致。

‧hashCode的使用時機

hashCode(雜湊碼)代表了一個物件的編碼,常用於物件的比較上,JAVA中的每個物件皆繼承了預設的hashCode函數,但若兩個相同內容的物件卻分別位於不同的記憶體位置,以預設的hashCode函數編碼結果不保證一致,若希望僅以物件的內容進行編碼,需覆寫hashCode函數。

在JDK的java.util套件中,包含了常用的集合(Set)或樹(Tree)資料結構,Set中常用到的有TreeSet及HashSet ,HashSet常用於儲存並且篩選重複物件,以上述所提及Foo物件為例:

class Foo {
    public final String name;
    public final int age;

    public Foo(String name,int age) {
       this.name = name;
       this.age = age;
    }
}

若不覆寫hashCode及equals的情況下,希望相同內容的物件不會加入HashSet:

Foo foo1 = new Foo("A",20);
Foo foo2 = new Foo("A",20);
Foo foo3 = foo1;

HashSet hs = new HashSet();
hs.add(foo1);
System.out.println(hs.size());
hs.add(foo2);
System.out.println(hs.size());
hs.add(foo3);
System.out.println(hs.size());

會印出

1
2
2

在物件HashSet中,將物件加入HashSet前需經過檢查,確保Set中不會有重複的物件出現,呼叫函數add時,此函數首先呼叫傳入物件的hashCode,並與HashSet中每個已加入物件的hashCode比對,若某個HashSet中的物件之hashCode與傳入物件一致,再以equals函式比對兩者是否一致,因此加入foo2時,HashSet將foo2及foo1視為不同的物件,使得foo2被加入HashSet。

欲修正此問題,僅需覆寫equals及hashCode如:

class Foo {
    public final String name;
    public final int age;

    public Foo(String name,int age) {
       this.name = name;
       this.age = age;
    }
   
    public boolean equals(Object obj) {
      if (obj != null && obj instanceof Foo)
        if (this.name != null && this.name.equals( ( (Foo) obj).name))
          if (this.age == ( (Foo) obj).age)
            return true;

      return false;
    }
   
    public int hashCode() {
      return name.hashCode() << (age % 8) * age;
    }
}

執行上述程式碼,會得到:

1
1
1

由輸出結果可知,HashSet中僅加入了foo1物件,並將foo2與foo3視為與foo1一致,所以HashSet大小為1,如此一來可確保HashSet中不會有重複內容的物件出現。


arrow
arrow
    全站熱搜

    bbkb 發表在 痞客邦 留言(2) 人氣()