JWorld@TW the best professional Java site in Taiwan
      註冊 | 登入 | 全文檢索 | 排行榜  

» JWorld@TW » Java SE 討論區  

按列印兼容模式列印這個話題 列印話題    把這個話題寄給朋友 寄給朋友   
reply to topicthreaded modego to previous topicgo to next topic
本主題所含的標籤
無標籤
作者 Interface VS Abstract class [精華]
Duncan

還隱隱作痛

版主

發文: 7816
積分: 39
於 2003-08-31 18:07 user profilesend a private message to usersend email to Duncanreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Interface 和 Abstract class 的差異,我想各位都有一定的瞭解,雖然在別的 forum 看過有人問起這個問題,我不打算在此討論兩者的細節。我打算只提及一些我個人對二者的感覺。

Interface 和 Class 兩者皆可引進 type 的觀念,前者純粹是定義了由一組介面所構成的規格,本身無法承載實做細節;後者本身也能帶來 type 的定義,同時還能附上實做的細節,abstract class 是一種 class(abstract class 甚至可以沒有任何 abstract method),同樣具備 class 所有的條件。這裡我強調的是後者(class)本身帶來 type(規格)這一件事。

為什麼要強調這一點呢?當你寫出如下的程式碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface IX {
  public Object narrow();
}
 
public class XY implements IX {
  public static void main(String[] args)
  {
    XY obj = new XY();
    System.out.println(obj.narrow());
  }
 
  public Object narrow()
  {
    return this;
  }
}


XY implements IX 也定義了和 IX 所明列的操作(這是必然的,編譯器要求設計者做到這一點)。
基本上你操作 obj 所參考的物件是以 XY(type) 的觀點(規格)來操作,main method 裡沒有 IX interface 的參與,即使你沒有讓 XY implements IX,main method 所產生的 instructions 和上例所產生的是一模一樣,並不會因為 XY implements IX 而使得在 invoke 符合 IX 所明列的操作介面的那一點上編譯器"主動"把物件(XY instance)視為一種 IX 規格的東西。我對這一點的看法是,implements IX 只是通知編譯器在必要的時刻允許 VM 將 XY instance 當作是 IX 規格的東西,"XY implements IX" 表示出 XY 相容於 IX 規格(所以編譯器要求一定要明白實做出 IX 所有的介面,否則只能成為 abstract class)。儘管如此,narrow method 的確成為了 XY 的一個(操作)介面,我的意思是這並不是因為 XY implements IX 不得不實做出這樣的一個(操作)介面所帶來的結果,而是因為 XY 本身也定義(宣告)了 narrow 這樣一個(操作)介面而使得 XY 相容於 IX,回顧此段一開始所提及,即使 IX interface 根本不存在而 XY 沒有 implemets IX,main method 一樣能操作的好好(因為 instructions 完全沒變),不會有 runtime error,XY 能執行 narrow 操作不是拜 IX 之賜,純粹是 XY "自己"定義並實做了這個操作。可能乍聽之下有點意外,我的意思是:因為 XY 定義了 narrow (操作)介面,所以可以明列出 XY 是相容於 IX 這一點(implements IX)並在適當時刻把 XY object 當作 IX 規格之物,而不是反過來的因果關係,因為 implements IX,所以 XY 要定義並實做 narrow (操作)介面,雖然這樣說很合理,但我覺得這樣可能會帶來一些誤導。

現在出現了一個 XYZ 類別 extends XY 並 implements 其他的 interface but IX,XYZ 透過 extends 的繼承機制同時從 XY 竊取了規格(type)與實做(implementation),即便 XYZ 不作什麼就可以把 narrow 的實做碼變成自己的一部份(其實是可以向 XY 借用),因而 XYZ (一定)可以相容於 IX 規格,從這一點來看,可能會比較清楚我在上一段所強調的東西。現在我產生了 XYZ object 並執行其 narrow 操作:
1
2
3
XYZ obj = new XYZ();
// or XY obj = new XYZ();
obj.narrow();

上一段例子一樣,這幾行完全沒出現 IX 的影子,也沒受到 IX 帶來的影響,拿掉 IX 並不指明 XY 相容於 IX ,還是跑得好好的。(到這裡還是不清楚我的意思也無大礙,繼續下去就會慢慢清楚了)

照我所講的 "implements" 帶來 class 相容某個 interface 的資料與保證,可以在必要的時刻讓 VM 把 XY instance 當作 IX 規格之物來用,什麼是必要時刻呢?
1
2
3
4
5
6
XY obj = new XY();
System.out.println(((IX)obj).narrow());
/* or
 * IX obj = new XY();
 * System.out.println(obj.narrow());
 */

產生出來的 instructions 和 mark 掉的部分相同,(((IX)ob).narrow(); 此一 statement 並不會真的作 checkcast 的動作,類似 class 之間的 casting 的道理)只是語意上不大一樣,前者編譯器會以 XY 的規格來對待 new XY() 所建構的物件,"偶爾"臨時以 IX 規格來對待;後者則是直接以 IX 規格來對待新建構的物件。

正式進入主題了,這一次我想要比較什麼呢?基本上 class 和 inteface 本質就差異蠻大的,即使 abstract class 和 interface 之間還是差很多,所以我真正的焦點在於退化的 abstract class,一個只擁有(自己只定義) abstract method 的 pure abstract class(講 pure 也不太恰當,畢竟還是至少得繼承 java.lang.Object,就會有 concrete method)。一個 interface 可以改寫成 pure abstract class,用起來在多數情況下以 programmer 角度來看是相同的,除了 interface 可以隨時混搭到其他的 type hierarchy 裡,pure abstract class 不行,但是後者的資料承載比較強。如果現在有一份規格已訂出來了,你會以 pure abstract class 來訂製還是做成一個 interface,我會選擇做成 interface,基於彈性的考量,可能你也是吧!不過在看完下面的對抗之後,可能或多或少會影響你的抉擇。

對抗的是執行效率,因為以不同的觀點來看帶同一個物件並執行同一操作所使用的 instruction 不同,很明顯在效率上一定有差別。
1
2
3
4
XY obj = new XY();
IX x = obj;
obj.narrow(); // used instruction: invokevirtual
x.narrow();   // used instruction: invokeinterface


在實際測量前,我大概知道輸家是 invokeinterface,因為其需要從 stack 取出的 actual argument 總是比 invokevirtual 多一個(多一個用來指示要從 stack 取出的參數數量),花的時間很難不多一點。

我把測試用的相關類別 post 一份在這裡方便直接瀏覽,也可以按此下載

IX.java
1
2
3
4
5
package com.jsptw.example.interfac;
 
public interface IX {
  public void action();
}


CX.java
1
2
3
4
5
package com.jsptw.example.jabstrac;
 
public abstract class CX {
  public abstract void action();
}


XObject.java
1
2
3
4
5
6
7
8
9
10
package com.jsptw.example;
 
import com.jsptw.example.interfac.IX;
import com.jsptw.example.jabstrac.CX;
 
public class XObject extends CX implements IX{
  public void action()
  {
  }
}


Measurement.java
1
2
3
4
5
package com.jsptw.example;
 
public abstract class Measurement {
  public abstract long evaluate(XObject x, int freq);
}


ForInterface.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.jsptw.example.interfac;
 
import com.jsptw.example.Measurement;
import com.jsptw.example.XObject;
 
public class ForInterface extends Measurement {
  public long evaluate(XObject x, int freq)
  {
    IX obj = x;
    long start = System.currentTimeMillis();
    for (int i = 0; i < freq; ++i)
      obj.action();
    long end = System.currentTimeMillis();
    
    return end - start;
  }
 
}


ForAbstractC.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.jsptw.example.jabstrac;
 
import com.jsptw.example.Measurement;
import com.jsptw.example.XObject;
 
public class ForAbstractC extends Measurement {
  public long evaluate(XObject x, int freq)
  {
    CX obj = x;
    long start = System.currentTimeMillis();
    for (int i = 0; i < freq; ++i)
      obj.action();
    long end = System.currentTimeMillis();
    
    return end - start;
  }
 
}


MeasureProc.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.jsptw.example;
 
public class MeasureProc {
  public static final int UNIT = 1000000;
  
  public MeasureProc(XObject sample, Measurement m, int iterations)
  {
    this.sample = sample;
    this.iterations = iterations;
    measurement = m;
  }
  
  public MeasureProc(XObject sample, Measurement m)
  {
    this(sample, m, 1);
  }
  
  public MeasureProc(XObject sample)
  {
    this(sample, null);
  }
 
  public int getIterations()
  {
    return iterations;
  }
 
  public void setIterations(int i)
  {
    iterations = i;
  }
  
  public Measurement getMeasurement()
  {
    return measurement;
  }
 
  public void setMeasurement(Measurement measurement)
  {
    this.measurement = measurement;
  }
  
  public long getResult()
  {
    if (measurement == null)
      return 0;
    return measurement.evaluate(sample, iterations);
  }
  
  private Measurement measurement;
  private XObject sample;
  private int iterations;
}


附帶一提,我已經儘量做到公平,讓兩者的 package+class name 一樣長(這和 VM 搜尋 callee 有關)。另外我在計算時間時把 loop counter 的操作所好時間一起加進去(因為沒辦法把每次執行 action method 的時間分次計算然後求和),當然也包括了執行 System.currentTimeMillis() 本身所需要的時間(取得截止時間時)。

以下是結果:(powered by JFreeChart 0.9.8)


我本來也想順便測試在其他的 Java Platform(J2ME) 的各個 Configuration(使用不同的 VM, KVM/CVM 之類的)下作測試,後來想想還是把這樣的機會讓給有興趣也對其他相關技術熟悉的人。

--
這樣的話題會不會很 boring?


Duncan edited on 2007-06-04 01:05
reply to postreply to post

給我
辣味豆腐 其餘免談
作者 Re:Interface VS Abstract class [Re:Duncan]
iampoya

Speculator

版主

發文: 169
積分: 8
於 2003-09-01 09:46 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
即是想要運用OO的場合
何必在意那數百ms的效率
彈性與效率本來不就是互斥?

剛好看到一篇主題相似的文章
http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox_p.html
不過該文關注的焦點是在彈性


reply to postreply to post
Japan Adult Video Album
作者 Re:Interface VS Abstract class [Re:iampoya]
Duncan

還隱隱作痛

版主

發文: 7816
積分: 39
於 2003-09-01 13:20 user profilesend a private message to usersend email to Duncanreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
iampoya wrote:
即是想要運用OO的場合
何必在意那數百ms的效率
彈性與效率本來不就是互斥?

剛好看到一篇主題相似的文章
http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox_p.html
不過該文關注的焦點是在彈性

"彈性與效率本來不就是互斥?" 這是自然的道理,但你可以思考一下其他的程式語言(C++)如何在這二者身上取得妥協。C++ 使用 virtual function 達到 polymorphism,換來了彈性但付出的代價只有一點點,只是多一個間接取值的動作。

我有提到我個人是比較注重彈性。
文中提出的比較結果並不是區區幾百 ms 的問題,如果以這樣的觀點來看:測試的時間不單單只是 invokeinterface/invokevirtual 單一 instruction 的執行時間,還包括 loop counter 的操作在內,兩個 case 被測量的 instructions 相同(但數量不只一個)而相異的 instruction 只有一個(也就是我要比較的),這樣的結果並不是 invokeinterface 效率慢 invokevirtual 兩倍而已,因為如果我們試著把兩者相同的 instructions 所耗用的時間減掉,剩下單獨 invokeinterface/virtual 所消耗的時間,可以發現兩者的效能差異可能在 10 被以上,你看到的是一個極短的時間差異(幾百個 ms),我看到的是兩者的倍數關係。

再從另一個角度來看,如果 Java 支援 multi-inheritance,interface 還有存在的必要嗎?基本上可以用 pure abstract class 來代替 interface(C++ 就是以 pure abstract class 來表示 interface 的概念),而且不會有太大的 overhead,也不會提高多重繼承(多重介面)的設計難度。事實是主導者選擇了 interface 而捨棄 multi-inheritance 的支援,引進了支援 interface 的 instruction - invokeinterface,但是其效率卻(可能)差了 pure abstract class 所使用的 invokevirtual 十倍。


reply to postreply to post

給我
辣味豆腐 其餘免談
作者 Re:Interface VS Abstract class [Re:Duncan]
popcorny

Jakarta 2%

版主

發文: 752
積分: 20
於 2003-09-01 15:16 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Duncan wrote:
再從另一個角度來看,如果 Java 支援 multi-inheritance,interface 還有存在的必要嗎?基本上可以用 pure abstract class 來代替 interface(C++ 就是以 pure abstract class 來表示 interface 的概念),而且不會有太大的 overhead,也不會提高多重繼承(多重介面)的設計難度。事實是主導者選擇了 interface 而捨棄 multi-inheritance 的支援,引進了支援 interface 的 instruction - invokeinterface,但是其效率卻(可能)差了 pure abstract class 所使用的 invokevirtual 十倍。


Duncan這篇看interface跟abstract class的角度挺特別的...
以往我們只以功能面來看
這篇是以效能的角度去看...
我也是在此篇才知道interface跟abstract class還有效率上的差異

但是我針對你這段提出一些我的看法
想請問的是.. 若java支援multi-inheritance..
那multi-inheritance之invokevirtual的效率
會跟現在的invokevirtual的效率會一樣嘛??
還是會差十倍?

如果有了解c++ object model
當初他們為了要能夠multiple (implementation) inheritance..(多重實作繼承)
他們的物件模型會非常的複雜...
如.. 位移在哪裡阿... 還有Diamond狀的繼承要怎麼處裡阿...
各field的指標要怎麼指阿....
這些overhead絕對多出很多...
這在java中的single implementation inheritance.. (單一實作繼承)..
是沒有的overhead

你可以想像現在的invokevirtual是最佳化的virtual function
這個最佳化是因為single implementation inheritance帶來的
以致於效能會比invokeinterace好
但是我不認為java若支援multiple implementation inheritancec效率會比較好
至少invokevirtual考慮的問題就多出很多了..
效率只會更差
這是我的論點

ps. implementation inheritance (實作繼承) 是相對於 interface inheritance..(介面繼承)
在java中... 只支援單一實作繼承... 而透過interface來支援多重介面繼承


popcorny edited on 2003-09-01 15:23
reply to postreply to post
作者 Re:Interface VS Abstract class [Re:popcorny]
Duncan

還隱隱作痛

版主

發文: 7816
積分: 39
於 2003-09-01 21:23 user profilesend a private message to usersend email to Duncanreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
popcorny wrote:
但是我針對你這段提出一些我的看法
想請問的是.. 若java支援multi-inheritance..
那multi-inheritance之invokevirtual的效率
會跟現在的invokevirtual的效率會一樣嘛??
還是會差十倍?


這個很難說,效率變差我想一定會,但是在考慮這樣的情形時應該認定 interface 不存在(也就是一個 class 只有 extends 一個或數個 class 而不會同時又 implements interface)。效率會差多少還需要再多加考慮。

比較難搞的反而是設計上的問題,多重實做繼承會有一些煩人的問題,我想 sun 主要是希望 Java 簡潔易用才做出這樣的決定,而不是因為效率的考量。

popcorny wrote:
如果有了解c++ object model
當初他們為了要能夠multiple (implementation) inheritance..(多重實作繼承)
他們的物件模型會非常的複雜...
如.. 位移在哪裡阿... 還有Diamond狀的繼承要怎麼處裡阿...
各field的指標要怎麼指阿....
這些overhead絕對多出很多...
這在java中的single implementation inheritance.. (單一實作繼承)..
是沒有的overhead

ps. implementation inheritance (實作繼承) 是相對於 interface inheritance..(介面繼承)
在java中... 只支援單一實作繼承... 而透過interface來支援多重介面繼承


上述提到的 overhead 在 C++ 這種半動態繫結的語言裡,都是編譯時期的事由編譯器直接算出偏移量,並不會在 runtime 造成負擔。同樣的事搬到動態繫結的 Java 裡,的確情況變的複雜,runtime 的負荷也變大。由於 Java 的動態連結特性,type 的檢驗都轉移到 runtime,如果 ancestor type hierarchy 成為樹狀結構會使得型別檢驗花很多的時間(我也想不到比較有效率的方法,雖然 invokevirtual 本身的參數可以指出 callee method 定義在哪一個 class 裡,但這對於執行環境在 runtime 去檢驗某個 object 是否合乎指定的介面,帶來的幫助不大)。

現在我想 Java 摒棄多重繼承除了多重繼承本身帶來的困難度之外,執行效率也是很重要的原因。


reply to postreply to post

給我
辣味豆腐 其餘免談
作者 Re:Interface VS Abstract class [Re:Duncan]
hambking





發文: 39
積分: 2
於 2003-09-02 01:05 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
這個話題不會很boring呀...

我覺得蠻有趣的,先前我以為我也只注意彈性......

沒想到performance的差異會差這麼多.....

很感謝duncan大大肯花時間po這篇文章......Smile


reply to postreply to post
作者 Re:Interface VS Abstract class [Re:hambking]
leo88813





發文: 63
積分: 0
於 2005-04-25 15:29 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
完全看不懂阿阿阿....可能是層次不到,畢竟我連SCJP都還沒拿到...唉...

reply to postreply to post
作者 Re:Interface VS Abstract class [Re:Duncan]
tree671025

Dragon



發文: 63
積分: 1
於 2005-05-10 15:03 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
哇~~只顧著看基本面的東西
每次只想到api提供了啥咪功能
從沒想到那麼學術面的去看java
感謝Duncan版大提供的另一個領域!!


reply to postreply to post
▂▃▄▅▆▇█TRY TO BE A MAN█▇▆▅▄▃▂
我的JAVA第一歨--->看完所有精華區
我的JAVA第二歨--->看懂所有精華區
我的JAVA第三歨--->暫時還沒想到 =.=
作者 Re:Interface VS Abstract class [Re:Duncan]
beneo





發文: 252
積分: 0
於 2008-07-21 00:45 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
我看了覺得有道理,但是看了還是有不明白的地方。比如TYPE,我一直沒有搞清楚這個東西是個什麽意思。

reply to postreply to post

在Javaworld.tw裡面,我很崇拜D和M,他們很熱心,我也很喜歡HK2K,雖然看起來他很裝B......
作者 Re:Interface VS Abstract class [Re:beneo]
heimo





發文: 102
積分: 0
於 2011-03-10 11:49 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
很有意思的一篇文章
現在才看到
謝謝:>

Cool


reply to postreply to post
作者 Re:Interface VS Abstract class [Re:Duncan]
wureka





發文: 37
積分: 0
於 2011-03-18 21:39 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
感謝分享。
小弟受教很多。


reply to postreply to post
作者 Re:Interface VS Abstract class [Re:Duncan]
betterxlife





發文: 29
積分: 0
於 2011-03-19 00:48 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
編譯時期的事由編譯器直接算出偏移量,並不會在 runtime 造成負擔。
-------------------------
這句話點出了重點~但我在想的是要怎麼實做?
因為java編譯出來的bytecode並沒有像c有這麼堅固的連接性~java看起來像是個別的結構在執行時產生關聯(就像沒有link一樣)~而c在編譯連結時就已將關連產生(也就不需要考慮Excption)~
就我看起來java其實有點像是半編譯半直譯~比較像是java架構上的瓶頸~除非改架構~不然我想就算java實做了多重繼承~也未必能增加速度


reply to postreply to post
» JWorld@TW »  Java SE 討論區

reply to topicthreaded modego to previous topicgo to next topic
  已讀文章
  新的文章
  被刪除的文章
Jump to the top of page

JWorld@TW 本站商標資訊

Powered by Powerful JuteForum® Version Jute 1.5.8