?? 對象相等比較深入equals方法-java面向對象 - it電子教育門戶 高端java培訓.htm
字號:
= new MyTest(10);<BR>
System.out.println(fp.equals(fp1));<BR>
System.out.println(fp1.equals(fp));<BR> }<BR>}<BR>class
MyTest extends FieldPosition{<BR> int x = 10;<BR>
public MyTest(int x){<BR>
super(x);<BR> this.x = x;<BR>
}<BR> public boolean equals(Object
o){<BR> if(o==null) return
false;<BR> if(!(o instanceof MyTest ))
return false;<BR> return ((MyTest)o).x ==
this.x;<BR> }<BR>}</P>
<P>運行一下看看會打印出什么:</P>
<P>System.out.println(fp.equals(fp1));打印true<BR>System.out.println(fp1.equals(fp));打印flase</P>
<P>兩個對象,出現了不對稱的equals算法.問題出在哪里(腦筋急轉彎:當然出在JDK實現的BUG)?</P>
<P>我相信有太多的程序員(除了那些根本不知道實現equals方法的程序員外)在實現equals方法<BR>時都用過instanceof運行符來進行短路優化的,實事求是地說很長一段時間我也這么用過。<BR>太多的教程,文檔都給了我們這樣的誤導。而有些稍有了解的程序員可能知道這樣的優化可能<BR>有些不對但找不出問題的關鍵。另外一種極端是知道這個技術缺陷的骨灰級專家就提議不要這<BR>樣應用。</P>
<P>我們知道,"通常"要對兩個對象進行比較,那么它們"應該"是同一類型。所以首先利用instanceof<BR>運行符進行短路優化,如果被比較的對象不和當前對象是同一類型則不用比較返回false,但事實<BR>上,"子類是父類的一個實例",所以如果
子類 o instanceof
父類,始終返回true,這時肯定<BR>不會發生短路優化,下面的比較有可能出現多種情況,一種是不能造型成子類而拋出異常,另一種<BR>是父類的private
成員沒有被子類繼承而不能進行比較,還有就是形成上面這種不對稱比較。可能<BR>會出現太多的情況。</P>
<P><BR>那么,是不是就不能用
instanceof運行符來進行優化?答案是否定的,JDK中仍然有很多實現是正<BR>確的,如果一個class是final的,明知它不可能有子類,為什么不用
instanceof來優化呢?</P>
<P>為了維護SUN的開發小組的聲譽,我不說明哪個類中,但有一個小組成員在用這個方法優化時在后加<BR>加上了加上了這樣的注釋:</P>
<P> if (this ==
obj)
// quick
check<BR>
return true;<BR> if
(!(obj instanceof
XXXXClass)) //
(1) same
object?<BR>
return false;<BR>可能是有些疑問,但不知道如何做(不知道為什么沒有打電話給我......)</P>
<P>那么對于非final類,如何進行類型的quick check呢?</P>
<P>if(obj.getClass() != XXXClass.class) return false;</P>
<P>用被比較對象的class對象和當前對象的class比較,看起來是沒有問題,但是,如果這個類的子類<BR>沒有重新實現equals方法,那么子類在比較的時候,obj.getClass()
肯定不等于XXXCalss.class,<BR>也就是子類的equals將無效,所以if(obj.getClass() !=
this.getClass()) return false;才是正<BR>確的比較。<BR></P>
<P><FONT face=Verdana>另外一個quick check是if(this==obj) return
true;</FONT></P><FONT face=Verdana>
<P><BR>是否equals方法一定比較的兩個對象就一定是要同一類型?上面我用了"通常",這也是絕大多數程序<BR>員的愿望,但是有些特殊的情況,我們可以進行不同類型的比較,這并不違反規范。但這種特殊情況<BR>是非常罕見的,一個不恰當的例子是,Integer類的equals可以和Sort做比較,比較它們的value是不<BR>是同一數學值。(事實上JDK的API中并沒有這樣做,所以我才說是不恰當的例子)</P>
<P>在完成quick
check以后,我們就要真正實現你認為的“相等”。對于如果實現對象相等,沒有太高<BR>的要求,比如你自己實現的“人”類,你可以認為只要name相同即認為它們是相等的,其它的sex,<BR>ago都可以不考慮。這是不完全實現,但是如果是完全實現,即要求所有的屬性都是相同的,那么如<BR>何實現equals方法?</P>
<P>class Human{<BR> private String name;<BR> private
int ago;<BR> private String
sex;<BR>
....................<BR>
public boolean equals(Object obj){<BR> quick
check.......<BR> Human other = (Human)ojb;<BR>
return this.name.equals(other.name) <BR>
&& this.ago == ohter.ago<BR> &&
this.sex.equals(other.sex);<BR> }<BR>}</P>
<P>這是一個完全實現,但是,有時equals實現是在父類中實現,而要求被子類繼承后equals能正確的工<BR>作,這時你并不事實知道子類到底擴展了哪些屬性,所以用上面的方法無法使equals得到完全實現。<BR>一個好的方法是利用反射來對equals進行完全實現:</P>
<P> public boolean
equals(Object obj){<BR> quick check.......<BR>
Class c = this.getClass();<BR> Filed[] fds =
c.getDeclaredFields();<BR> for(Filed
f:fds){<BR>
if(!f.get(this).equals(f.get(obj)))<BR>
return false;<BR> }<BR> return
true;<BR> }<BR>為了說明的方便,上明的實現省略了異常,這樣的實現放在父類中,可以保證你的子類的equals可以按<BR>你的愿望正確地工作。</P>
<P>關于equals方法的最后一點是:如果你要是自己重寫(正確說應該是履蓋)了equals方法,那同時就一<BR>定要重寫hashCode().為是規范,否則.............<BR>我們還是看一下這個例子:</P>
<P>public final class PhoneNumber {<BR>
private final int areaCode;<BR> private
final int exchange;<BR> private final int
extension;</P>
<P> public PhoneNumber(int areaCode, int
exchange, int extension)
{<BR>
rangeCheck(areaCode, 999, "area
code");<BR>
rangeCheck(exchange, 99999999,
"exchange");<BR>
rangeCheck(extension, 9999,
"extension");<BR>
this.areaCode =
areaCode;<BR>
this.exchange =
exchange;<BR>
this.extension = extension;<BR> }</P>
<P> private static void rangeCheck(int arg,
int max, String name)
{<BR> if(arg < 0
|| arg >
max)<BR>
throw new IllegalArgumentException(name + ": " +
arg);<BR> }</P>
<P> public boolean equals(Object o)
{<BR> if(o ==
this)<BR>
return true;<BR>
if(!(o instanceof
PhoneNumber))<BR>
return false;<BR>
PhoneNumber pn =
(PhoneNumber)o;<BR>
return pn.extension == extension && pn.exchange ==
exchange && pn.areaCode ==
areaCode;<BR> }<BR>}</P>
<P>注意這個類是final的,所以這個equals實現沒有什么問題。</P>
<P>我們來測試一下:</P>
<P> public static void main(String[] args)
{<BR> Map hm = new
HashMap();<BR>
PhoneNumber pn = new PhoneNumber(123, 38942,
230);<BR> hm.put(pn,
"I love you");<BR>
PhoneNumber pn1 = new PhoneNumber(123, 38942,
230);<BR>
System.out.println(pn);<BR>
System.out.println("pn.equals(pn1) is " +
pn.equals(pn1));<BR>
System.out.println(hm.get(pn1));<BR>
System.out.println(hm.get(pn));<BR>
}<BR>既然pn.equals(pn1),那么我put(pn,"I love
you");后,get(pn1)這什么是null呢?<BR>答案是因為它們的hashCode不一樣,而hashMap就是以hashCode為主鍵的。</P>
<P>所以規范要求,如果兩個對象進行equals比較時如果返回true,那么它們的hashcode要求返回相等的值。</FONT></P></FONT></DIV></SPAN></TD></TR>
<TR>
<TD width="71%"> </TD>
<TD width="29%">【 <A
href="http://www.mldn.cn/print.jtml?articleid=1283">打印</A> 】【
<A
href="http://www.mldn.cn/member/favlist.jtml?action=add&postid=1283">收藏</A>
】【 <A href="http://www.mldn.cn/email.jtml?articleid=1283"
target=_blank>推薦</A> 】 </TD></TR>
<TR>
<TD colSpan=2>
<TABLE cellSpacing=3 cellPadding=0 width="100%" align=center
bgColor=#ffffff border=0>
<TBODY>
<TR height=25>
<TD align=middle width="33%"><IMG height=95 alt=java視頻教程
src="對象相等比較深入equals方法-JAVA面向對象 - IT電子教育門戶 高端JAVA培訓.files/javavideo.gif"
width=642></TD></TR>
<TR>
<TD class=content-table vAlign=top align=middle>
<TABLE>
<TBODY>
<TR>
<TD>
<TABLE>
<TBODY>
<TR>
<TD><A
href="http://www.mldn.cn/articleview/2007-8-21/article_view_2251.htm"
rel=external><IMG class=midImg alt=struts2.0入門視頻
src="對象相等比較深入equals方法-JAVA面向對象 - IT電子教育門戶 高端JAVA培訓.files/struts2.gif"
width=90></A></TD></TR>
<TR>
<TD style="HEIGHT: 22px"> <A
title=struts2.0入門視頻
href="http://www.mldn.cn/articleview/2007-8-21/article_view_2251.htm"
rel=external>struts2.0入門視頻</A></TD></TR></TBODY></TABLE></TD>
<TD>
<TABLE>
<TBODY>
<TR>
<TD><A
href="http://www.mldn.cn/articleview/2007-6-15/article_view_2141.htm"
rel=external><IMG class=midImg
alt=JAVAEE學習流程和學習方法
src="對象相等比較深入equals方法-JAVA面向對象 - IT電子教育門戶 高端JAVA培訓.files/j2eejc.gif"
width=90></A></TD></TR>
<TR>
<TD style="HEIGHT: 22px"> <A
title=JAVAEE學習流程和學習方法
href="http://www.mldn.cn/articleview/2007-6-15/article_view_2141.htm"
rel=external>JAVAEE學習流程和學習方..</A></TD></TR></TBODY></TABLE></TD>
<TD>
<TABLE>
<TBODY>
<TR>
<TD><A
href="http://www.mldn.cn/articleview/2007-6-5/article_view_2091.htm"
rel=external><IMG class=midImg
alt=1-Java介紹及JDK配置
src="對象相等比較深入equals方法-JAVA面向對象 - IT電子教育門戶 高端JAVA培訓.files/javase.gif"
width=90></A></TD></TR>
<TR>
<TD style="HEIGHT: 22px"> <A
title=1-Java介紹及JDK配置
href="http://www.mldn.cn/articleview/2007-6-5/article_view_2091.htm"
rel=external>1-Java介紹及JDK配置..</A></TD></TR></TBODY></TABLE></TD>
<TD>
<TABLE>
<TBODY>
<TR>
<TD><A
href="http://www.mldn.cn/articleview/2007-4-19/article_view_2012.htm"
rel=external><IMG class=midImg alt=Oracle中的多表連接
src="對象相等比較深入equals方法-JAVA面向對象 - IT電子教育門戶 高端JAVA培訓.files/Oracle.gif"
width=90></A></TD></TR>
<TR>
<TD style="HEIGHT: 22px"> <A
title=Oracle中的多表連接
href="http://www.mldn.cn/articleview/2007-4-19/article_view_2012.htm"
rel=external>Oracle中的多表連接</A></TD></TR></TBODY></TABLE></TD>
<TD>
<TABLE>
<TBODY>
<TR>
<TD><A
href="http://www.mldn.cn/articleview/2007-4-11/article_view_1978.htm"
rel=external><IMG class=midImg
alt=Struts中logic標簽的使用
src="對象相等比較深入equals方法-JAVA面向對象 - IT電子教育門戶 高端JAVA培訓.files/struts.gif"
width=90></A></TD></TR>
<TR>
<TD style="HEIGHT: 22px"> <A
title=Struts中logic標簽的使用
href="http://www.mldn.cn/articleview/2007-4-11/article_view_1978.htm"
rel=external>Struts中logic標簽..</A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD colSpan=2>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -