Java 8的方法参考进一步限制了重载
方法重載一直是一個充滿喜憂參半的話題。 我們已經在博客上介紹了它,并介紹了幾次警告:
- 您會后悔對Lambdas應用重載!
- 保持干燥:方法重載
- 為什么每個人都討厭操作員超載
- API設計師,請小心
重載有用的主要原因有兩個:
Bot原因僅僅是出于為API使用者提供便利的目的。 在JDK中很容易找到很好的例子:
默認參數
public class Integer {public static int parseInt(String s) {return parseInt(s,10);}public static int parseInt(String s, int radix) {} }在上面的示例中,第一個parseInt()方法只是使用最常用的基數調用第二個方法的一種簡便方法。
析取參數類型替代
有時,使用不同類型的參數可以實現相似的行為,這意味著相似的事物,但在Java的類型系統中不兼容。 例如,當構造一個String :
public class String {public static String valueOf(char c) {char data[] = {c};return new String(data, true);}public static String valueOf(boolean b) {return b ? "true" : "false";}// and many more... }如您所見,根據參數類型優化了相同方法的行為。 由于兩個valueOf()方法的語義相同,因此在讀寫源代碼時這不會影響該方法的“感覺”。
此技術的另一個用例是常用時,相似但不兼容的類型需要在彼此之間方便地轉換。 作為API設計人員,您不想讓這樣繁瑣的轉換使您的API消費者感到無聊。 相反,您提供:
public class IOUtils {public static void copy(InputStream input, OutputStream output);public static void copy(InputStream input, Writer output);public static void copy(InputStream input, Writer output, String encoding);public static void copy(InputStream input, Writer output, Charset encoding); }這是一個很好的示例,它同時顯示了默認參數(可選編碼)以及參數類型替代項( OutputStream與Writer或String與Charset編碼表示形式)。
邊注
我懷疑聯合類型和默認參數船很早以前就已經為Java航行了-盡管聯合類型可能被實現為語法糖,但是默認參數將是引入JVM的野獸,因為它將取決于JVM對Java的缺少支持。命名參數。
正如Ceylon語言所顯示的那樣,這兩個功能覆蓋了所有方法重載用例的99%,這就是為什么Ceylon可以在不重載的情況下完全完成-在JVM之上!
超載是危險且不必要的
上面的示例表明,重載實質上只是幫助人們與API交互的一種手段。 對于運行時,沒有重載之類的東西。 調用僅以字節碼“靜態”鏈接到不同的,唯一的方法簽名(給出或采用更新的操作碼,例如invokedynamic)。 但是要點是,對于上述計算機,如果上述方法全部都稱為copy() ,或者被明確地調用了m1() , m2() , m3()和m4() ,則對計算機沒有區別。
另一方面,重載在Java源代碼中是真實的,并且編譯器必須做大量工作才能找到最具體的方法,否則將應用JLS的復雜重載解析算法。 每個新的Java語言版本都會使情況變得更糟。 例如,在Java 8中,方法引用將給API使用者帶來更多痛苦,并且需要API設計人員的額外照顧。 考慮一下Josh Bloch的以下示例:
//發現bug靜態void pfc(List <Integer> x){x.stream()。map(Integer :: toString).forEach(s-> System.out.println(s.charAt(0))); }
— Joshua Bloch(@joshbloch) 2015年7月20日
您可以將上面的代碼復制粘貼到Eclipse中,以驗證編譯錯誤(請注意,最新的編譯器可能會報告類型推斷副作用,而不是實際錯誤)。 Eclipse為以下簡化報告了編譯錯誤:
static void pfc(List<Integer> x) {Stream<?> s = x.stream().map(Integer::toString); }…是
Ambiguous method reference: both toString() and toString(int) from the type Integer are eligible糟糕!
上面的表達是模棱兩可的。 它可以表示以下兩個表達式之一:
// Instance method: x.stream().map(i -> i.toString());// Static method: x.stream().map(i -> Integer.toString(i));可以看出,使用lambda表達式而不是方法引用可以立即解決歧義。 解決此歧義(朝向實例方法)的另一種方法是改用toString()的超類型聲明,該聲明不再模糊不清:
// Instance method: x.stream().map(Object::toString);結論
API設計者的結論非常清楚:
自Java 8以來,方法重載已成為API設計人員更加危險的工具。
盡管上述內容并非真正“嚴峻”,但當API使用者的編譯器拒絕看似正確的代碼時,他們將花費大量時間來克服這種認知上的摩擦。 從該示例中獲取的一個大人造假菜是:
切勿混合使用類似的實例和靜態方法重載
實際上,這會放大您的靜態方法重載何時重載java.lang.Object的名稱, 正如我們在先前的博客文章中所解釋的那樣 。
遵循以上規則很簡單。 因為只有兩個有效的重載原因(默認參數和不兼容的參數替代),所以沒有必要為同一類中的方法提供靜態重載。 一個更好的設計(如JDK所公開)是具有“伴侶類”的,類似于Scala的伴侶對象。 例如:
// Instance logic public interface Collection<E> {} public class Object {}// Utilities public class Collections {} public final class Objects {}通過更改方法的名稱空間,可以在某種程度上很好地規避重載,并且不會出現以前的問題。
TL; DR:避免超載,除非增加的便利性真正增加了價值!
翻譯自: https://www.javacodegeeks.com/2015/08/java-8s-method-references-put-further-restrictions-on-overloading.html
總結
以上是生活随笔為你收集整理的Java 8的方法参考进一步限制了重载的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 歌词刮风这天 歌词刮风这天的完整歌词
- 下一篇: 数据湖 多维数据集_按汇总分组/多维数据