java8 默认方法_默认方法:Java 8的无名英雄
java8 默認方法
幾周前,我寫了一個博客,說開發人員學習新語言是因為它們很酷。 我仍然堅持這個主張,因為關于Java 8的事情真的很酷。 盡管毫無疑問,該節目的明星是添加了Lambdas和將函數提升為一等變量,但我目前最喜歡的是默認方法。 這是因為它們是在不破壞舊代碼的情況下向現有接口添加新功能的一種巧妙方法。
實現很簡單:采用一個接口,添加一個具體方法,并將關鍵字default附加為修飾符。 結果是您的接口的所有現有實現突然都可以使用此代碼。 在第一個簡單示例中,我添加了默認方法,該方法返回接口1的版本號。
public interface Version { /** * Normal method - any old interface method: * * @return Return the implementing class's version */ public String version(); /** * Default method example. * * @return Return the version of this interface */ default String interfaceVersion() { return "1.0"; } }然后,您可以在任何實現類上調用此方法。
public class VersionImpl implements Version { @Override public String version() { return "My Version Impl"; } }您可能會問:為什么這很酷? 如果采用java.lang.Iterable接口并添加以下默認方法,則會死于for循環。
default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } }forEach方法采用實現Consumer<T>接口作為參數的類的實例。 Consumer<T>可以在新的java.util.function包中找到,它是Java 8稱為功能接口的功能 ,該接口僅包含一個方法。 在這種情況下,方法accept(T t)接受一個參數并返回一個void 。
java.util.function包可能是Java 8中最重要的包之一。它包含一堆描述通用函數類型的單一方法或函數接口。 例如, Consumer<T>包含一個接受一個參數并返回void的函數,而Predicate<T>是一個包含一個接受一個參數并返回boolean的函數的接口,通常用于編寫過濾lambda。
該接口的實現應包含您先前在for循環括號之間編寫的內容。
那么,您可能會想,這給了我什么? 如果不是Java 8,那么答案是“不多”。 要在Java 8之前使用forEach(…)方法,您需要編寫如下代碼:
List<String> list = Arrays.asList(new String[] { "A", "FirsT", "DefaulT", "LisT" }); System.out.println("Java 6 version - anonymous class"); Consumer<String> consumer = new Consumer<String>() { @Override public void accept(String t) { System.out.println(t); } }; list.forEach(consumer);但是,如果將其與lambda表達式或方法引用結合使用,則可以編寫一些看起來很酷的代碼。 使用方法參考,前面的示例變為:
list.forEach(System.out::println);您可以使用lambda表達式執行相同的操作:
list.forEach((t) -> System.out.println(t));所有這些似乎都與Java 8背后的一個重要思想保持一致:讓JDK為您完成工作。 用政治家和連環友約翰·肯尼迪(John F Kennedy)的話來形容“不要問您對JDK可以做什么,請問您的JDK可以為您做什么” 2 。
默認方法的設計問題
那是編寫無處不在的for循環的一種很酷的新方法,但是在接口中添加默認方法是否存在問題?如果是的話,它們是什么?Java 8項目中的人如何修復它們?
首先要考慮的是繼承。 當您擁有一個擴展了另一個接口的接口并且兩個接口都具有帶有相同簽名的默認方法時,會發生什么? 例如,如果您具有由MiddleInterface擴展的SubInterface和由SuperInterface擴展的MiddleInterface , SubInterface怎么SubInterface ?
public interface SuperInterface { default void printName() { System.out.println("SUPERINTERFACE"); } }public interface MiddleInterface extends SuperInterface { @Override default void printName() { System.out.println("MIDDLEINTERFACE"); } }public interface SubInterface extends MiddleInterface { @Override default void printName() { System.out.println("SUBINTERFACE"); } }public class Implementation implements SubInterface { public void anyOldMethod() { // Do something here } public static void main(String[] args) { SubInterface sub = new Implementation(); sub.printName(); MiddleInterface middle = new Implementation(); middle.printName(); SuperInterface sup = new Implementation(); sup.printName(); } }無論用哪種方式剪切, printName()都將始終打印“ SUBINTERFACE”。
當您具有包含相同方法簽名的類和接口時,會出現相同的問題:哪個方法在運行? 答案是“階級勝利”法則。 接口默認方法將始終被類方法所忽略。
public interface AnyInterface { default String someMethod() { return "This is the interface"; } }public class AnyClass implements AnyInterface { @Override public String someMethod() { return "This is the class - WINNING"; } }運行上面的代碼將始終打印出:“這是課程-WINNING”
最后,如果一個類實現兩個接口并且都包含具有相同簽名的方法,會發生什么? 這是古老的C ++鉆石問題 ; 您如何解決歧義? 運行哪種方法?
public interface SuperInterface { default void printName() { System.out.println("SUPERINTERFACE"); } }public interface AnotherSuperInterface { default void printName() { System.out.println("ANOTHERSUPERINTERFACE"); } }在Java 8的情況下,答案都不是。 如果您嘗試同時實現這兩個接口,則會收到以下錯誤:
Duplicate default methods named printName with the parameters () and () are inherited from the types AnotherSuperInterface and SuperInterface.在絕對必須實現兩個接口的情況下,解決方案是調用“類獲勝”規則并覆蓋實現中的歧義方法。
public class Diamond implements SuperInterface, AnotherSuperInterface { /** Added to resolve ambiguity */ @Override public void printName() { System.out.println("CLASS WINS"); } public static void main(String[] args) { Diamond instance = new Diamond(); instance.printName(); } }何時使用默認方法
從純粹的角度來看,默認方法的添加意味著Java接口不再是接口。 接口被設計為用于擬議/預期行為的規范或合同:實施類必須履行的合同。 添加默認方法意味著接口和抽象基類之間實際上沒有區別3 。 這意味著它們容易受到濫用,因為一些經驗不足的開發人員可能認為從其代碼庫中刪除基類并用基于默認方法的接口替換它們很酷–只是因為它們可以,而其他人可能只是將抽象類與實現默認值的接口混淆了方法。 我目前建議僅將默認方法用于其預期的用例:在不破壞現有代碼的情況下改進傳統接口。 雖然我可能會改變主意。
1它不是很有用,但是它說明了一點……
2約翰·肯尼迪(John F Kennedy)的就職演說1961年1月20日。
3抽象基類可以具有構造函數,而接口則不能。 類可以具有私有實例變量(即狀態)。 接口不能。
翻譯自: https://www.javacodegeeks.com/2014/08/default-methods-java-8s-unsung-heros.html
java8 默認方法
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的java8 默认方法_默认方法:Java 8的无名英雄的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我在小米笔记本上用上了华为鸿蒙小米会使用
- 下一篇: java美元兑换,(Java实现) 美元