Tips
《Effective Java, Third Edition》一書英文版已經出版,這本書的第二版想必不少人都讀過,號稱Java四大名著之一,不過第二版2009年出版,到如今已經將近8年的時間,但隨着Java 6,7,8,甚至9的發佈,Java語言發生了深入的變化。
在這裏第一時間翻譯成中文版。供你們學習分享之用。java
在Java 8以前,不可能在不破壞現有實現的狀況下爲接口添加方法。 若是向接口添加了一個新方法,現有的實現一般會缺乏該方法,從而致使編譯時錯誤。 在Java 8中,添加了默認方法( default method)構造[JLS 9.4],目的是容許將方法添加到現有的接口。 可是增長新的方法到現有的接口是充滿風險的。程序員
默認方法的聲明包含一個默認實現,該方法容許實現接口的類直接使用,而沒必要實現默認方法。 雖然在Java中添加默認方法能夠將方法添加到現有接口,但不能保證這些方法能夠在全部已有的實現中使用。 默認的方法被「注入(injected)」到現有的實現中,沒有通過實現類的知道或贊成。 在Java 8以前,這些實現是用默認的接口編寫的,它們的接口永遠不會得到任何新的方法。apache
許多新的默認方法被添加到Java 8的核心集合接口中,主要是爲了方便使用lambda表達式(第6章)。 Java類庫的默認方法是高質量的通用實現,在大多數狀況下,它們工做正常。 可是,編寫一個默認方法並不老是可能的,它保留了每一個可能的實現的全部不變量。函數
例如,考慮在Java 8中添加到Collection接口的removeIf
方法。例如,考慮在Java 8中添加到Collection接口的removeIf
方法。此方法刪除給定布爾方法(或Predicate
函數式接口)返回true的全部元素。默認實現被指定爲使用迭代器遍歷集合,調用每一個元素的謂詞,並使用迭代器的remove
方法刪除謂詞返回true的元素。 據推測,這個聲明看起來像這樣:默認實現被指定爲使用迭代器遍歷集合,調用每一個元素的Predicate
函數式接口,並使用迭代器的remove
方法刪除Predicate函數式接口返回true的元素。 根據推測,這個聲明看起來像這樣:學習
// Default method added to the Collection interface in Java 8 default boolean removeIf(Predicate<? super E> filter) { Objects.requireNonNull(filter); boolean result = false; for (Iterator<E> it = iterator(); it.hasNext(); ) { if (filter.test(it.next())) { it.remove(); result = true; } } return result; }
這是可能爲removeIf
方法編寫的最好的通用實現,但遺憾的是,它在一些實際的Collection實現中失敗了。 例如,考慮org.apache.commons.collections4.collection.SynchronizedCollection
方法。 這個類出自Apache Commons類庫中,與java.util包中的靜態工廠Collections.synchronizedCollection
方法返回的類類似。 Apache版本還提供了使用客戶端提供的對象進行鎖定的能力,以代替集合。 換句話說,它是一個包裝類(條目 18),它們的全部方法在委託給包裝集合類以前在一個鎖定對象上進行同步。測試
Apache的SynchronizedCollection
類仍然在積極維護,但在撰寫本文時,並未重寫removeIf
方法。 若是這個類與Java 8一塊兒使用,它將繼承removeIf
的默認實現,但實際上不能保持類的基本承諾:自動同步每一個方法調用。 默認實現對同步一無所知,而且不能訪問包含鎖定對象的屬性。 若是客戶端在另外一個線程同時修改集合的狀況下調用SynchronizedCollection
實例上的removeIf
方法,則可能會致使ConcurrentModificationException
異常或其餘未指定的行爲。ui
爲了防止在相似的Java平臺類庫實現中發生這種狀況,好比Collections.synchronizedCollection
返回的包級私有的類,JDK維護者必須重寫默認的removeIf
實現和其餘相似的方法來在調用默認實現以前執行必要的同步。 原來不屬於Java平臺的集合實現沒有機會與接口更改進行相似的改變,有些尚未這樣作。線程
在默認方法的狀況下,接口的現有實現類能夠在沒有錯誤或警告的狀況下編譯,但在運行時會失敗。 雖然不是很是廣泛,但這個問題也不是一個孤立的事件。 在Java 8中添加到集合接口的一些方法已知是易受影響的,而且已知一些現有的實現會受到影響。翻譯
應該避免使用默認方法向現有的接口添加新的方法,除非這個須要是關鍵的,在這種狀況下,你應該仔細考慮,以肯定現有的接口實現是否會被默認的方法實現所破壞。然而,默認方法對於在建立接口時提供標準的方法實現很是有用,以減輕實現接口的任務(條目 20)。設計
還值得注意的是,默認方法不是被用來設計,來支持從接口中移除方法或者改變現有方法的簽名的目的。在不破壞現有客戶端的狀況下,這些接口都不可能發生更改。
準則是清楚的。 儘管默認方法如今是Java平臺的一部分,可是很是悉心地設計接口仍然是很是重要的。 雖然默認方法能夠將方法添加到現有的接口,但這樣作有很大的風險。 若是一個接口包含一個小缺陷,可能會永遠惹怒用戶。 若是一個接口嚴重缺陷,可能會破壞包含它的API。
所以,在發佈以前測試每一個新接口是很是重要的。 多個程序員應該以不一樣的方式實現每一個接口。 至少,你應該準備三種不一樣的實現。 編寫多個使用每一個新接口的實例來執行各類任務的客戶端程序一樣重要。 這將大大確保每一個接口都能知足其全部的預期用途。 這些步驟將容許你在發佈以前發現接口中的缺陷,但仍然能夠輕鬆地修正它們。 雖然在接口被髮布後可能會修正一些存在的缺陷,但不要太期望這一點。