<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    我的漫漫程序之旅

    專注于JavaWeb開發
    隨筆 - 39, 文章 - 310, 評論 - 411, 引用 - 0
          以上幾條規則并不是最完整的表述,詳細的請參見API文檔.對于Object類,它提供了一個最最嚴密的實現,那就是只有是同一對象時,equals方法才返回true,也就是人們常說的引用比較而不是值比較.這個實現嚴密得已經沒有什么實際的意義, 所以在具體子類(相對于Object來說)中,如果我們要進行對象的值比較,就必須實現自己的equals方法.先來看一下以下這段程序:

       
     public boolean equals(Object obj)
        
    {
            
    if (obj == nullreturn false
    ;
            
    if (!(obj instanceof
     FieldPosition))
                
    return false
    ;
            FieldPosition other 
    =
     (FieldPosition) obj;
            
    if (attribute == null
    {
                
    if (other.attribute != null
    {
                    
    return false
    ;
                }

            }

            
    else if (!attribute.equals(other.attribute)) {
                
    return false
    ;
            }

            
    return (beginIndex == other.beginIndex
                
    && endIndex ==
     other.endIndex
                
    && field ==
     other.field);
        }
     

         這是JDK中java.text.FieldPosition的標準實現,似乎沒有什么可說的. 我信相大多數或絕大多數程序員認為,這是正確的合法的equals實現.畢竟它是JDK的API實現啊. 還是讓我們以事實來說話吧:

    package debug;
    import java.text.*
    ;
    public class Test 
    {
      
    public static void main(String[] args) 
    {
        FieldPosition fp 
    = new FieldPosition(10
    );
        FieldPosition fp1 
    = new MyTest(10
    );
        System.out.println(fp.equals(fp1));
        System.out.println(fp1.equals(fp));
      }

    }

    class MyTest extends FieldPosition{
      
    int x = 10
    ;
      
    public MyTest(int x)
    {
        
    super
    (x);
        
    this.x =
     x;
      }

      
    public boolean equals(Object o){
        
    if(o==nullreturn false
    ;
        
    if(!(o instanceof MyTest )) return false
    ;
        
    return ((MyTest)o).x == this
    .x;
      }


    }


    運行一下看看會打印出什么:
    System.out.println(fp.equals(fp1));打印true
    System.out.println(fp1.equals(fp));打印flase

          兩個對象,出現了不對稱的equals算法.問題出在哪里(腦筋急轉彎:當然出在JDK實現的BUG)?我相信有太多的程序員(除了那些根本不知道實現equals方法的程序員外)在實現equals方法時都用過instanceof運行符來進行短路優化的,實事求是地說很長一段時間我也這么用過。

         太多的教程,文檔都給了我們這樣的誤導。而有些稍有了解的程序員可能知道這樣的優化可能有些不對但找不出問題的關鍵。另外一種極端是知道這個技術缺陷的骨灰級專家就提議不要這樣應用。我們知道,"通常"要對兩個對象進行比較,那么它們"應該"是同一類型。所以首先利用instanceof運算符進行短路優化,如果被比較的對象不和當前對象是同一類型則不用比較返回false。

        但事實上,"子類是父類的一個實例",所以如果子類 o instanceof 父類,始終返回true,這時肯定不會發生短路優化,接下來的比較(即子類與父類比較)有可能出現多種情況,一種是不能造型成父類而拋出異常,另一種是父類的private 成員沒有被子類繼承而不能進行比較,還有就是形成上面這種不對稱比較。可能會出現太多的情況。

        那么,是不是就不能用 instanceof運算符來進行優化?答案是否定的,JDK中仍然有很多實現是正確的,如果一個class是final的,明知它不可能有子類,為什么不用 instanceof來優化呢?為了維護SUN的開發小組的聲譽,我不說明哪個類中,但有一個小組成員在用這個方法優化時加上了這樣的注釋:
     

           if (this == obj)                      // quick check
                return true;
            
    if (!(obj instanceof XXXXClass))         // (1) same object?

                return false;



         可能是有些疑問,但不知道如何做(不知道為什么沒有打電話給我......)那么對于非final類,如何進行類型的quick check呢?

    if(obj.getClass() != XXXClass.classreturn false;



         用被比較對象的class對象和當前對象的class比較,看起來是沒有問題,但是,如果這個類的子類沒有重新實現equals方法,那么子類在比較的時候,obj.getClass() 肯定不等于XXXCalss.class, 也就是子類的equals將無效(兩個子類對象比較總是false),所以

    if(obj.getClass() != this.getClass()) return false;



    才是正確的比較。另外一個quick check是if(this==obj) return true;

        是否equals方法比較的兩個對象一定是要同一類型?上面我用了"通常",這也是絕大多數程序員的愿望,但是有些特殊的情況,我們可以進行不同類型的比較,這并不違反規范。但這種特殊情況是非常罕見的,一個不恰當的例子是,Integer類的equals可以和Sort做比較,比較它們的value是不是同一數學值。(事實上JDK的API中并沒有這樣做,所以我才說是不恰當的例子)在完成quick check以后,我們就要真正實現你認為的“相等”。對于如何實現對象相等,沒有太高的要求,比如你自己實現的“人”類,你可以認為只要name相同即認為它們是相等的,其它的sex, ago都可以不考慮。這是不完全實現,但是如果是完全實現,即要求所有的屬性都是相同的,那么如何實現equals方法?

    class Human{
       
    private
     String name;
       
    private int
     ago;
       
    private
     String sex;
            ..
      
    public boolean equals(Object obj)
    {
        quick check.
        Human other 
    =
     (Human)ojb;
        
    return this.name.equals(other.name)  && this.ago == ohter.ago  && this
    .sex.equals(other.sex);
     }

    }



        這是一個完全實現,但是,有時equals實現是在父類中實現,而要求被子類繼承后equals能正確的工作,這時你并不事先知道子類到底擴展了哪些屬性,所以用上面的方法無法使equals得到完全實現。
    一個好的方法是利用反射來對equals進行完全實現:  

     

    public boolean equals(Object obj){
      quick check.
      Class c 
    = this
    .getClass();
      Filed[] fds 
    =
     c.getDeclaredFields();
      
    for(Filed f:fds)
    {
       
    if(!f.get(this
    ).equals(f.get(obj)))
        
    return false
    ;
      }

      
    return true;
     }


         為了說明的方便,上明的實現省略了異常,這樣的實現放在父類中,可以保證你的子類的equals可以按你的愿望正確地工作。關于equals方法的最后一點是:如果你要是自己重寫(正確說應該是履蓋)了equals方法,那同時就一定要重寫hashCode().這是規范,否則.............

    我們還是看一下這個例子:

    public final class PhoneNumber {
        
    private final int
     areaCode;
        
    private final int
     exchange;
        
    private final int
     extension;  

      
    public PhoneNumber(int areaCode, int exchange, int extension) 
    {
            rangeCheck(areaCode, 
    999"area code"
    );
            rangeCheck(exchange, 
    99999999"exchange"
    );
            rangeCheck(extension, 
    9999"extension"
    );
            
    this.areaCode =
     areaCode;
            
    this.exchange =
     exchange;
            
    this.extension =
     extension;
        }
      

      
    private static void rangeCheck(int arg, int max, String name) 
    {
            
    if(arg < 0 || arg >
     max)
                
    throw new IllegalArgumentException(name + "" +
     arg);
        }
      

        
    public boolean equals(Object o) 
    {
            
    if(o == this
    )
                
    return true
    ;
            
    if(!(o instanceof
     PhoneNumber))
                
    return false
    ;
            PhoneNumber pn 
    =
     (PhoneNumber)o;
            
    return pn.extension == extension && pn.exchange == exchange && pn.areaCode ==
     areaCode;
        }

    }



    注意這個類是final的,所以這個equals實現沒有什么問題。我們來測試一下:   

     

    public static void main(String[] args) {
            Map hm 
    = new
     HashMap();
            PhoneNumber pn 
    = new PhoneNumber(12338942230
    );
            hm.put(pn, 
    "I love you"
    );
            PhoneNumber pn1 
    = new PhoneNumber(12338942230
    );
            System.out.println(pn);
            System.out.println(
    "pn.equals(pn1) is " +
     pn.equals(pn1));
            System.out.println(hm.get(pn1));
            System.out.println(hm.get(pn));
        }


         既然pn.equals(pn1),那么我put(pn,"I love you")后,get(pn1)為什么是null呢?
    答案是因為它們的hashCode不一樣,而hashMap就是以hashCode為主鍵的。所以規范要求,如果兩個對象進行equals比較時如果返回true,那么它們的hashcode要求返回相等的值。



    數據加載中……

    深入研究equals方法

     equals方法的重要性毋須多言,只要你想比較兩個對象是不是同一對象,你就應該實現equals方法,讓對象用你認為相等的條件來進行比較.

         下面的內容只是API的規范,沒有什么太高深的意義,但我之所以最先把它列在這兒,是因為這些規范在事實中并不是真正能保證得到實現.
    1.對于任何引用類型, o.equals(o) == true成立.

    2.如果 o.equals(o1) == true 成立,那么o1.equals(o)==true也一定要成立.

    3.如果 o.equals(o1) == true 成立且  o.equals(o2) == true 成立,那么
      o1.equals(o2) == true 也成立.

    4.如果第一次調用o.equals(o1) == true成立,在o和o1沒有改變的情況下以后的任何次調用都成立.

    5.o.equals(null) == true 任何時間都不成立.

    posted on 2008-01-05 16:21 々上善若水々 閱讀(467) 評論(0)  編輯  收藏 所屬分類: J2SE

    主站蜘蛛池模板: 亚洲av综合av一区| 亚洲色图综合在线| 成人黄动漫画免费网站视频| 日本zzzzwww大片免费| 免费无码又爽又刺激毛片| 免费少妇a级毛片人成网| 国产l精品国产亚洲区在线观看| 亚洲成人黄色在线观看| 亚洲AV无码一区二区三区鸳鸯影院| 免费大片黄在线观看| 91在线手机精品免费观看| mm1313亚洲国产精品美女| 亚洲AV福利天堂一区二区三| 国产成人亚洲精品91专区高清| 久久精品国产免费观看| 亚洲一区二区免费视频| 三上悠亚电影全集免费| 成人爽A毛片免费看| 亚洲AV美女一区二区三区| 一级毛片免费观看不卡视频| 国产小视频免费观看| 亚洲另类精品xxxx人妖| 无码午夜成人1000部免费视频| 精品亚洲成AV人在线观看| 无码毛片一区二区三区视频免费播放| 在线观看免费视频资源| 亚洲v高清理论电影| 美女视频黄免费亚洲| 日韩精品亚洲人成在线观看| 免费成人激情视频| 国产亚洲视频在线| 四虎影在线永久免费观看| 美丽的姑娘免费观看在线播放| 亚洲国产成人综合| 一本久久综合亚洲鲁鲁五月天| 久久er国产精品免费观看8| 免费看小12萝裸体视频国产| 精品熟女少妇aⅴ免费久久| 亚洲国产成人精品久久| 国产免费观看a大片的网站| 日韩免费的视频在线观看香蕉|