本人考慮了這個問題很久,在網上也看過很多資料,這里講一下我的理解。
1. 多繼承
C++ 中有多繼承的概念,即一個類可以有多個直接父類。這么做很靈活,但是如果兩個父類中有同名方法或者同名的函數,就比較麻煩了(指定訪問域)。Java 的一個好處是只允許一個類有一個直接父類,(部分原因是)考慮到彌補沒有多繼承帶來的一些問題,有了接口。
2. 接口與類
接口可以看作是一種特殊的類,接口與類的重要區別是,接口中的方法必須要重寫(接口中類方法默認抽象),而類不同,子類即便是不重寫父類的方法,也是沒有問題的,甚至可以通過 super 關鍵字,調用父類的方法。這也意味着,接口比起類要更加規范,要用接口這個“父類”,必須要重寫其中的方法。
另一方面,接口是相對簡單的,只用提供接口方法即可,實現的邏輯可以交給繼承了接口的類來完成。另外,接口比抽象類要好,原因也在於接口允許多繼承。
試想,一個嬰兒類,如果繼承了手接口、腳接口等,最后實現的就會是一個健康的嬰兒,要修改各個部分的功能也比較簡單(增加接口方法,或者修改類中方法邏輯即可),如果是繼承一個 Human 類,如果父類中有一部分出現問題,子類就會出問題,而且人類的所有功能糅雜在一個類中,功能也不好划分。接口則提供了一種功能規范,實現了這種規范就能夠實現一種功能,並且邏輯是繼承了接口的類負責實現的,降低了接口本身出錯的可能性。
同屬某一父類的子類間的差異性(什么時候用接口?)
接口較之於類,可以實現更低層次上的抽象。例如動物類,作為父類,可以有一個 move 方法(所有動物都可以移動),但是對於同屬於動物的豬、獅子類,如何從功能上區別這兩個類?一個辦法是在原父類和子類之間再加上三個類:肉食動物類、草食動物類、雜食動物類,每個動物類都有選擇地去繼承這幾個類。
但是這樣做會有問題,因為繼承級數的增加,方法名、變量名都有可能出現同名的情況,原因在於父類的某些字段或者屬性,對子類來說有可能是不可見的,子類實現時,無法對父類的情況完全可見,有可能造成沖突。
接口則不同,接口想要被繼承,其中的接口方法一定是要暴露出來的,子類也必須要全部實現這些方法,只要按照規范來做事,就可以避免出錯的情況。繼續分析上面的例子,如果把肉食動物類、草食動物類、雜食動物類改為三個接口,獅子繼承肉食動物接口,實現其中的 hunt (捕獵)方法;豬實現雜食動物接口,實現其中的方法;這樣就可以讓獅子類和豬類同屬於動物類(最高級別抽象),但又分別實現了不同的接口(不同的食性,低一級的抽象)。
總結
接口的好處有很多,用它的理由也有很多,需要在實戰中去感受。我認為從抽象的級別來理解接口與類是比較好的辦法。