2.1 預設方法
關鍵字 default 允許在接口中提供方法實作——在 Java 8 之前被禁止。當在接口中使用它時,任何實作接口卻沒有定義方法的時候可以使用 default 建立的方法體。
在 Java 8 之前,這些定義要在每個實作中重複實作,顯得多餘且令人煩惱。
預設方法比抽象類中的方法受到更多的限制,但是非常有用。
如果我們在接口中增加一個新方法 newMethod(),而在實作類中沒有實作它,編譯器就會報錯。如果我們使用關鍵字 default 為 newMethod() 方法提供預設的實作,那麼所有與接口有關的代碼能正常工作,不受影響,而且這些代碼還可以調用新的方法 newMethod()。
增加預設方法的極具說服力的理由是它允許在不破壞已使用接口的代碼的情況下,在接口中增加新的方法。預設方法有時也被稱為守衛方法或虛拟擴充方法。
2.2 多繼承
一個類可能從多個父類型中繼承特征和特性。
Java 在設計之初,C++ 的多繼承機制飽受诟病。Java 過去是一種嚴格要求單繼承的語言:隻能繼承自一個類(或抽象類),但可以實作任意多個接口。在 Java 8 之前,接口沒有包袱——它隻是方法外貌的描述。
現在,Java 通過預設方法具有了某種多繼承的特性。結合帶有預設方法的接口意味着結合了多個基類中的行為。因為接口中仍然不允許存在屬性(隻有靜态屬性),是以屬性仍然隻會來自單個基類或抽象類,即不會存在狀态的多繼承。
當兩種接口中有相同簽名的方法,子類實作他倆時,需要覆寫沖突的方法:可以重定義 jim() 方法,也能使用 super 關鍵字選擇基類實作中的一種。
2.3 靜态方法
Java 8 允許在接口中添加靜态方法。這麼做能恰當地把工具功能置于接口中,進而操作接口,或者成為通用的工具。
這是模版方法設計模式的一個版本,一個模版方法。
3 抽象類和接口
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iYlBjY0ETZzUWNmRDN5UWO4QWZ0QDM5YWO0MGO0ETNj9CX5d2bs92Yl1iclB3bsVmdlR2LcNWaw9CXt92Yu4GZjlGbh5yYjV3Lc9CX6MHc0RHaiojIsJye.png)
抽象類仍然是一個類,在建立新類時隻能繼承它一個。而建立類的過程中可以實作多個接口。
3.1 盡可能地抽象
是以,更傾向使用接口而不是抽象類。
隻有當必要時才使用抽象類。
除非必須使用,否則不要用接口和抽象類。
大多數時候,普通類已經做得很好,如果不行的話,再移動到接口或抽象類中。
4 完全解耦
當方法操縱的是一個類而非接口時,它就隻能作用于那個類或其子類。如果想把方法應用于那個繼承層級結構之外的類,就會觸黴頭。接口在很大程度上放寬了這個限制,将接口與實作解耦使得接口可以應用于多種不同的實作,因而代碼更具可複用性。
5 多接口組合
接口沒有任何實作——也就是說,沒有任何與接口相關的存儲——是以無法阻止組合多接口。這是有價值的,因為你有時需要表示“一個 x 是一個 a 和一個 b 以及一個 c”。
子類并不要求必須繼承自abstract的或“具體的”(沒有任何抽象方法)的父類。如果繼承一個非接口類,那麼隻能繼承一個類,其餘的基元素必須都是接口。需要将所有的接口名稱置于 implements 關鍵字之後且用逗号分隔。可以有任意多個接口,并可以向上轉型為每個接口,因為每個接口都是獨立的類型。
類結合了具體類和接口。當通過這種方式結合具體類和接口時,需要将具體類放在前面,後面跟着接口(否則編譯器會報錯)。
使用接口的核心原因之一:為了能夠向上轉型為多個基類型(以及由此帶來的靈活性)。
然而,使用接口的第二個原因與使用抽象基類相同:防止用戶端程式員建立這個類的對象,確定這僅僅隻是一個接口。
這帶來了一個問題:應該使用接口還是抽象類呢?
如果建立不帶任何方法定義或成員變量的基類,就選擇接口而不是抽象類。事實上,如果知道某事物是一個基類,可以考慮用接口實作它。
6 使用繼承擴充接口
通過繼承,可以很容易
在接口中增加方法聲明
在新接口中結合多個接口
這兩種情況都可以得到新接口。
6.1 多接口時的命名沖突
當實作多個接口時可能會存在一個小陷阱。之前說到完全相同的方法沒有問題,但是如果它們的簽名或傳回類型不同會怎麼樣呢?
覆寫、實作和重載會令人不快地攪和在一起、。同時,重載方法僅根據傳回類型是區分不了的。是以,當打算組合接口時,在不同的接口中使用相同的方法名通常會造成代碼可讀性的混亂,盡量避免這種情況。
7 接口适配
接口最吸引人的原因之一是相同的接口可以有多個實作。
在簡單情況下展現在一個方法接受接口作為參數,該接口的實作和傳遞對象給方法則交由你來做。
是以,接口的一種常見用法是政策設計模式。
編寫一個方法執行某些操作并接受一個指定的接口作為參數。可以說:“隻要對象遵循接口,就可以調用方法” ,這使得方法更加靈活,通用,并更具可複用性。
例如,類 Scanner 的構造器接受的是一個 Readable 接口。
你會發現 Readable 沒有用作 Java 标準庫中其他任何方法的參數——它是單獨為 Scanner 建立的,是以 Scanner 沒有将其參數限制為某個特定類。通過這種方式,Scanner 可以與更多的類型協作。如果你建立了一個新類并想讓 Scanner 作用于它,就讓它實作 Readable 接口。
假設你有一個類沒有實作 Readable 接口,怎樣才能讓 Scanner 作用于它呢?
可以再次使用擴充卡模式,通過關鍵字 interface 提供的多繼承。因為你可以以這種方式在已有類中增加新接口,是以這就意味着一個接受接口類型的方法提供了一種讓任何類都可以與該方法進行适配的方式。這就是使用接口而不是類的強大之處。