java提供了抽象類和介面,總體是好事。
有的OOP語言並沒有介面的概念,但相當一部分其實用其它方式實現了JAVA中介面類似的功能。
如果不太清楚二者的區別,難免在面臨具體業務的時候,在二者之間搖擺。
---
實際上,關於抽象類和介面的共同點和不同點沒有什麼可以寫的。
設計原則原則讓我們儘量基於介面程式設計(IOP-Interface-Oriented Programming),而不是基於具體類,所以多用介面。
如果需要繼承(通常就存在屬性),則多用抽象類。
---
一、基本概念
這裡有個難點:java的抽象類和介面已經不如發明者期望的那麼純粹了。
所以,以下的概念主要適用於J17及其以上版本。
2.1、抽象類
核心概念:無法例項化的類
用處:為子類提供模板,限定子類的實現,併為子類提供一些公共的且已經實現的方法/屬性。
可以包含內容:各種可見範圍的都可以,最典型的就是抽象方法。除了不能用default,好像就麼有不行的。
如果只定義了抽象方法,那麼它的作用基本等同於介面。
2.2、介面
核心概念:必須被實現的類,否則無用(但實際不是那麼回事,自從J9之後可以工具化)
用處:抽象以實現工程上的多型,從而實現模組話;為函數語言程式設計服務
可以包含內容:除了不能定義只包含public修飾符的方法,好像都可以。
二、比較
總體來說,java的抽象類和介面越發地同化了,除了以下幾個不能互相替代,好像沒有不能互換的:
a.抽象類的可以實現各種修飾符的方法或者屬性 -- 介面做不到,至少介面不能定義只有public修飾符的方法
b.介面可以用於實現函數語言程式設計;介面可以用於實現註解
抽象類更適合於定義帶有屬性的類,介面更適合用於只有實現方法的類(雖然可以帶靜態屬性)。
因為介面的這個特性,所以,介面很適合用於定義spring中bean,因為我們用bean的最主要目的就是為了實現單例的bean,
我們通常不關注特定的bean示例到底有什麼特性,只像知道某個bean示例能幹什麼。
所以,java這種和spring結合很深的語言,介面有大用處。
當然,在spring中,如果bean繼承抽象類來是實現bean的定義和注入也是可以的,不過這個時候,就不要為抽象類定義什麼屬性了。
以下就是這個演示(已經省略了包和import部分):
package study.base.oop.classes.modifier.abstractbean; /** * 抽象類,用於演示基於抽象類的 bean */ public abstract class AbstractLiterature { public abstract String writePoem(); } @Component public class ChineseLiteratureService extends AbstractLiterature { @Override public String writePoem() { return """ 兩個黃鸝鳴翠柳, 一行白鷺上青天。 醉裡挑燈看劍,夢迴吹角連營.八百里分麾下炙人,五十先翻塞外聲,沙場秋點兵。 """; } } @RequestMapping("/spring/bean/") @Controller public class BeanController { @Autowired private AbstractLiterature literatureService; @GetMapping("/abstract-bean") @ResponseBody public Object testAbstractBean() { String poem= this.literatureService.writePoem(); return poem; } }
編譯不會報錯,結果也可以看到:
所以,用介面還是用抽象類來介面定義,真是一個問題。
三、適用場景
3.1 想想要不要個性化的屬性
如前,如果想保留自有屬性,那麼用抽象類更好一些;如果不需要保留自有屬性,用介面更好一些。
實現一個bean的時候為什麼都是基本是實現一個介面,而不是擴充套件一個抽象類?
也許僅僅處於兩個考慮:
1.即使偏好抽象類,也需要避免有人往抽象類中新增屬性,變成不穩定的bean
2.僅僅基於一個樸素的想法:單例bean還要什麼屬性,所以用理論山乾淨一點的介面更好一些。
因為第2個因素的緣故,所以我們都會再三強調,不要在bean中定義可變的屬性/成員,即使要定義了,我們也是為了用這些
成員的能力而不是成員的資料。這樣做的最重要的目的是為了保證獲得方法的某種程度的冪等性。
一旦實現類的屬性(作為資料)參與方法計算,那麼由於單例的特性,可能會照成難於預料的結果,通常那不是我們期望
的結果。
為什麼單例bean的屬性會被共享? 這和JVM的記憶體模型有不可分割的關係。
3.2 介面特有的
至於註解和函數語言程式設計,現在只能求助於介面。
如果要實現多繼承,現在也只能求助於介面。
3.3預設使用介面的
具體到JAVA的經典應用場景-開發資訊應用系統後臺,那麼還是偏多使用介面。這是因為現在javaEE基本和Spring深度繫結,
spring和Bean深度繫結,Bean基本上就是單例bean,單例意味著不要特性化的屬性,不要屬性,那麼用介面無疑是最好不過了。
3.4 不用bean的地方-也許抽象類可以排上用場
當我們不用bean的時候,通常意味著要寫一些相對複雜一些的功能,這個時候抽象類可能更加方便一些。
如果需要定義公共的描述,並基於這個定義構建大模組,可以考慮用抽象類,或者普通的類也可以。
四、小結
雖然隨著JAVA版本的變更,介面和抽象類越發相像。
讓介面專享註解和函數語言程式設計,使得介面顯得有點獨大。
不過當你不想單例的時候,應該更多考慮抽象類。