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 - "多形" + "繼承"