2012年10月4日 星期四

abstract class和interface的差別與使用時機

abstract class 和 interface 的區別

abstract class的特徵:
1. abstract class和abstract method都必須用abstract關鍵字來修飾
2. abstract class不能用new關鍵字去產生物件
3. abstract method只需要宣告, 不需要實作
4. 繼承abstract class的子類別必須實作父類別的abstract method, 否則這子類別還是個abstract class

interface的特徵:
1. interface沒有建構方法 (即method中沒有參數, 且沒有任何變數的設定)
     Ex: interface Bus{
                void sound();
           }
2. interface中所有資料成員都必須初始化, 且均為常數
3. 宣告變數必須是final, static和public
4. interface中的method必須為abstact或public


 abstract class 和 interface 的使用時機

何謂abstract class?

提供一種多個class一起合作工作的方式, 將多個class中相同的元素pull up method到public class中, 再以繼承的方式來使用它, 目的是為了實現"多型"精神

Ex: 數學計算包括求各種形狀的面積體積 、數字的立方平方等, 就可以把類似的方法集中到同一個public class中

 abstract class Shape{
      String name;
      double length, width, heigh;
      double radius;

      abstract double area();            // 求面積
      abstract double perimeter();    // 求周長

      // abstract class中也可以有建構方法, 但必須在子類別中被呼叫
      // 四邊形的建構方法
      public Shape(double length, double width, String name){
            this.length = length;
            this.width = width;
            this.name = name;
      }
     
      // 三角形的建構方法
      public Shape(String name, double width, double height){
            this.height = height;
            this.width = width;
            this.name = name;
      }
}


class Rectangular extends Shape{      //長方形
     public Rectangular(double length, double width, String name){
            super(length, width, name);     //在子類別中呼叫父類別的
                                                          建構方法用super()
     } 

     //因為繼承Shape類別, 因此要實作area和perimeter的abstract class
     double area(){    
            return length * width;
     } 
    
     double perimeter(){
            return (length + width) * 2;
     }
}

class EqualTriangle extends Shape{      //三角形
      public EqualTrangle(String name, double width, double height){
              super(name, width, height);
      } 

      double area(){
            return (width * height) / 2 ;
     } 
    
     double perimeter(){
            return 3 * width;
     }
}

(註) 一個子類別只能繼承一個父類別, 但一個父類別可以被許多子類別所共同繼承 ---------------------------------------------------------------------------------------------------

何謂interface?

即spec., 完全不需要定義實作, 只需要函式原型
若要實作interface, 就必須follow它的spec.

[public] [abstract] interface 介面名稱{
    權限設定  傳回型態  method(parameters); 
    權限設定  傳回型態  method(parameters);
}
[public] [abstract]是預設, 所以可省略, 因為interface本身就是抽象的


(註)一個介面可以同時繼承多個介面, 即同時繼承了多個介面的abstract method和常數

 => interface A extends 介面1, 介面2, 介面3, ...

一個class可以同時實作多個interface
 => class B implements 介面1, 介面2, 介面3, ...


---------------------------------------------------------------------------------------------------
以另一篇範例來說明, 以下為轉貼自JavaWorld裡avseq的回文
連結在此: http://www.javaworld.com.tw/jute/post/view?bid=29&id=195263

實作時,Interface和Abstract Class的使用時機還蠻難拿捏的
我提供一下我實作時的經驗,如果有可以改進的地方,還希望一起討論

用例子應該比較好了解
本田(Honda)旗下有兩款車,Honda Civic和Honda Accord
這兩款車的輪胎大小,煞車系統,安全氣囊等裝置都一樣,只有引擎的啟動方式不一樣
這時可以定義一個抽象類別HondaCar ,並將引擎啟動的方法定義為abstact,強迫繼承的類別實作這個方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class HondaCar
{
    protected int tireSize = 12;
    
    protected abstract void engineStart();
    protected void stopCar()
    {
        // break method
    }
    
    protected void popAirBag()
    {
        //pop Air Bag
    }
}


1
2
3
4
5
6
7
8
9
10
public class Civic extends HondaCar
{
 
    protected void engineStart()
    {
        // Civic engine start method
 
    }
 
}


1
2
3
4
5
6
7
8
9
10
public class Accord extends HondaCar
{
 
    protected void engineStart()
    {
        // Accord engine start method
 
    }
 
}


為什麼要定義成抽象類別呢?
1)因為Civic和Accord的特徵幾乎都一樣,只有引擎的啟動方式不同
2)如果不把相同的程式碼定義在上層,這樣一來兩個類別都要寫重覆的程式碼,用複製貼上說不定還會貼錯
3)HondaCar宣告成抽象,是因為它不是一台具體的車,宣告成抽象可以避免這個類別被實體化。

假設現在小日本政府規定,任何車子都要加裝一個裝置,在每1000公里時都要將目前廢氣排放的情況回報給環保署的主機,
至於如何回報?可以WebService連結到環保署的主機,或是寫成一個file ftp到環保署等等..,也就是說,小日本政府不管你回報的方式是什麼就是要回報就對了。


此時可以定義一個interface,讓所有的車子都實作這個介面

1
2
3
4
public interface CarReporter
{
    public void report();
}

為什麼現在要用介面呢?
1)介面像是一個規範,所以實作的類別都要遵守這個規範,而不論繼承的類別實作方式是什麼。所以實作CarReporter的車子都有要回報的機制,至於怎麼回報,由各汽車廠自已決定。
2)Civic和Accord,它們的本質是汽車,而繼承關係是(is-a),如果用類別繼承的方式實作的話,會變成Civic是一個排氣回報裝置。所以應該利用實作介面的方式(like-a),也就是說Civic是一台有回報排氣裝置的汽車。
 


至於使用時機就看個人的設計了~看看Design Pattern會更清楚有哪些應用, 
較複雜一點的Pattern會使用interface + abstract class - "多形" + "繼承"



7 則留言:

Circle Hsiao 提到...

謝謝,對我有幫助。

lifeinvestment 提到...

thx for the message it helps

Unknown 提到...

很有幫助 感謝

Unknown 提到...

感謝,看很多文章都聽不太懂,看完你的例子就瞬間通了!!!

學習者 提到...

觀念上錯誤很多

1. interface的method可以設定參數,也可以事先定義好。
2. interface的成員不必初始化
3. interface中的變數不用宣告為final或static

以上,不確定是否是文章年代久遠,所以在觀念上與實際操作結果不符合。

匿名 提到...

簡易明瞭的舉例,謝謝。

baliracanelli 提到...

New Jersey's first legal sports betting, legal in 2021
It's 여주 출장마사지 not 강릉 출장안마 like some states, with legal online sports betting available to residents of New Jersey and Pennsylvania. As the only 상주 출장안마 state where online sports betting 구미 출장샵 is 나주 출장안마 legal,