默认方法:Java 8的无名英雄
幾周前,我寫了一個博客,說開發(fā)人員學習新語言是因為它們很酷。 我仍然堅持這個主張,因為關(guān)于Java 8的事情真的很酷。 毫無疑問,該節(jié)目的明星是添加了Lambdas以及將函數(shù)提升為一等變量,而我目前最喜歡的是默認方法。 這是因為它們是在不破壞舊代碼的情況下向現(xiàn)有接口添加新功能的一種巧妙方法。
實現(xiàn)很簡單:采用一個接口,添加一個具體方法,并將關(guān)鍵字default附加為修飾符。 結(jié)果是,接口的所有現(xiàn)有實現(xiàn)突然都可以使用此代碼。 在第一個簡單示例中,我添加了默認方法,該方法返回接口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"; } }然后,您可以在任何實現(xiàn)類上調(diào)用此方法。
public class VersionImpl implements Version { @Override public String version() { return "My Version Impl"; } }您可能會問:為什么這很酷? 如果采用java.lang.Iterable接口并添加以下默認方法,則會使for循環(huán)失效。
default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } }forEach方法采用實現(xiàn)Consumer<T>接口作為參數(shù)的類的實例。 Consumer<T>可以在新的java.util.function包中找到,它是Java 8所謂的功能接口 ,該接口僅包含一個方法。 在這種情況下,方法accept(T t)接受一個參數(shù)并且返回一個void 。
java.util.function軟件包可能是Java 8中最重要的軟件包之一。它包含一堆描述通用函數(shù)類型的單一方法或函數(shù)接口。 例如, Consumer<T>包含一個接受一個參數(shù)并返回void的函數(shù),而Predicate<T>是一個包含一個接受一個參數(shù)并返回boolean的函數(shù)的接口,通常用于編寫過濾lambda。
該接口的實現(xiàn)應包含您先前在for循環(huán)括號之間編寫的內(nèi)容。
那么,您可能會想,這給了我什么? 如果不是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表達式或方法引用結(jié)合使用,則可以編寫一些看起來很酷的代碼。 使用方法引用,前面的示例變?yōu)?#xff1a;
list.forEach(System.out::println);您可以使用lambda表達式執(zhí)行相同的操作:
list.forEach((t) -> System.out.println(t));所有這些似乎都與Java 8背后的一個重要思想保持一致:讓JDK為您完成工作。 用政治家和連環(huán)友約翰·肯尼迪(John F Kennedy)的話來形容“不要問您對JDK可以做什么,請問您的JDK可以為您做什么” 2 。
默認方法的設(shè)計問題
那是編寫無處不在的for循環(huán)的一種很酷的新方法,但是在接口中添加默認方法是否存在問題?如果是的話,它們是什么?Java 8項目中的人如何修復它們?
首先要考慮的是繼承。 當您擁有一個擴展了另一個接口的接口并且兩個接口都具有帶有相同簽名的默認方法時,會發(fā)生什么? 例如,如果您擁有由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”。
當您具有包含相同方法簽名的類和接口時,會出現(xiàn)相同的問題:哪個方法在運行? 答案是“階級勝利”法則。 接口默認方法將始終被類方法所忽略。
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”
最后,如果一個類實現(xiàn)兩個接口并且都包含具有相同簽名的方法,會發(fā)生什么? 這是古老的C ++鉆石問題 ; 您如何解決歧義? 運行哪種方法?
public interface SuperInterface { default void printName() { System.out.println("SUPERINTERFACE"); } }public interface AnotherSuperInterface { default void printName() { System.out.println("ANOTHERSUPERINTERFACE"); } }在Java 8的情況下,答案都不是。 如果您嘗試同時實現(xiàn)這兩個接口,則會收到以下錯誤:
Duplicate default methods named printName with the parameters () and () are inherited from the types AnotherSuperInterface and SuperInterface.在絕對必須實現(xiàn)兩個接口的情況下,解決方案是調(diào)用“類獲勝”規(guī)則并覆蓋實現(xiàn)中的歧義方法。
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接口不再是接口。 接口被設(shè)計為用于擬議/預期行為的規(guī)范或合同:實施類必須履行的合同。 添加默認方法意味著接口和抽象基類之間實際上沒有區(qū)別3 。 這意味著他們?nèi)菀资艿綖E用,因為一些經(jīng)驗不足的開發(fā)人員可能認為從其代碼庫中刪除基類并用基于默認方法的接口替換它們很酷–只是因為它們可以,而其他人可能只是將抽象類與實現(xiàn)默認值的接口混淆了方法。 我目前建議僅將默認方法用于其預期的使用情況:在不破壞現(xiàn)有代碼的情況下改進傳統(tǒng)接口。 雖然我可能會改變主意。
1它不是很有用,但是它說明了一點……
2肯尼迪(John F Kennedy)的就職演說1961年1月20日。
3抽象基類可以具有構(gòu)造函數(shù),而接口則不能。 類可以具有私有實例變量(即狀態(tài))。 接口不能。
翻譯自: https://www.javacodegeeks.com/2014/08/default-methods-java-8s-unsung-heros.html
總結(jié)
以上是生活随笔為你收集整理的默认方法:Java 8的无名英雄的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 揪的拼音和组词 揪的组词有哪些
- 下一篇: JavaFX技巧9:请勿混用Swing