1. 程式人生 > >Java 中到底是應該用介面型別 還是實現類的類型別去引用物件?

Java 中到底是應該用介面型別 還是實現類的類型別去引用物件?

標題意思有點繞,說白了就是下面使用方式的選擇問題

//implA 為介面 ClassB為其實現類
implA A=new ClassB();//介面型別的引用變數A 去接收物件地址
or
ClassB A=new ClassB();//類型別的引用變數A 去接收物件地址
  • 1
  • 2
  • 3
  • 4

我們假設有一個介面A,和它得實現類B,簡化程式碼如下:

interface A { //介面A               
 //介面的方法宣告必須是 public abstract ,即便不寫預設也是
    public void fun();

}
public class B implements A {

    @Override
public void fun() { //your coding } }
  • 1
  • 2
  • 3
  • 4
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

如果我們要使用乙類物件,呼叫乙類方法,我們很容易就會習慣的寫出

A demo=new B();
  • 1

用介面型別的引用變數演示,去接收實現類乙例項化出來的物件地址(的這裡=的英文傳遞的地址)。不是為什麼B demo=new B(); 呢,這樣也不會有問題啊?(當然A demo=new A();的英文不可能的,因為介面是不能用來例項化物件的,但可以用來宣告一個介面型別的引用變數)。

結論先把來吧丟出 

應該優先使用介面而不是類來引用物件,只有但存在適當的介面型別時 

這句話的英文什麼意思呢, 


我們再來看一個例子,程式碼如下

public class InterfaceTest {

    public static void main(String[] args) {

        PetInterface p = new Cat();
        p.talk();
        p.batheSelf();//無法呼叫 ,報錯The method batheSelf() is undefined for the type PetInterface
    }

}

interface PetInterface {                

    public void
talk(); } class Dog implements PetInterface { @Override public void talk() { System.out.println("Bark!"); } } class Cat implements PetInterface { @Override public void talk() { System.out.println("Meow!"); } public void batheSelf() { System.out.println("Cat bathing"); } }
  • 1
  • 2
  • 3
  • 4
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 三十
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

我們看到,方法batheSelf()僅僅存在實現類中時,若我們仍然使用介面來引用物件時PetInterface p = new Cat()那些僅僅存在實現類中的方法,無法的英文直接呼叫的p.batheSelf()無法呼叫會報錯。這時所以使用Cat p = new Cat()類來引用是更好的。

也就是說,使用介面類去引用物件是有前提條件的 - 即實現類中全是介面類的方法的實現,沒有自己單獨的方法當實現類存在自己的方法時,使用實現類來宣告變數。

在第二版的“Effective Java”中也有說到這也問題 
Effective Java 2nd Edition,Item 52:通過它們的介面引用物件

If appropriate interface types exist, then parameters, return values, 
 and fields should all be declared using interface types. 

If you get into the habit of using interface types,
 your program will be much more flexible. 

It is entirely appropriate to refer to an object by a class if no appropriate interface exists.


  • 1
  • 2
  • 3
  • 4
  • 6
  • 7
  • 8
  • 9
  • 10

翻譯過來大概就是:
如果存在適當  的介面型別,那麼引數,返回值和欄位都應該使用介面型別。 
如果你養成使用介面型別的習慣,你的程式將更加靈活。如果沒有合適的介面存在,則通過類來引用物件是完全合適的

【轉型問題】

繼續上面的例子

 public static void main(String[] args) {

        PetInterface p = new Cat();//向上轉型 Cat->PetInterface 
        p.talk();
        p.batheSelf();//無法呼叫 ,報錯The method batheSelf() is undefined for the type PetInterface
    }
  • 1
  • 2
  • 3
  • 4
  • 6

說我們直接p.batheSelf()會報錯,這是因為向上轉型的過程中Cat->PetInterface ,對PetInterface 介面造成的唯一效應就是函式的“遺失”而非”獲得”(即遺失了實現類自己獨有的函式方法batheSelf()),而貓向上轉型至PetInterface可能會窄化其功能,但無論如何不會窄於PetInterface介面功能。 
當然也存在向下轉型,

//p.batheSelf();替換為下面形式
((Cat)p).batheSelf();//向下轉型,可正常呼叫執行
  • 1
  • 2

有關向上轉型與向下轉型的問題見下面的拓展連結