Java 8 新特性:泛型目標型別推斷
泛型目標型別推斷
文 | 莫若吻
(注:以下內容是其他作者網上翻譯的資料,中文原創找不到了,無法給出原創譯文地址,覺得不錯就轉載過來了。)
1.簡單理解泛型
如果你還不瞭解什麼是泛型,請點選進入《泛型》詳細情況地址:
泛型是Java SE 1.5的新特性,泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。通俗點將就是“型別的變數”。這種型別變數可以用在類、介面和方法的建立中。當然最常見的就是用在集合中。
泛型可以節省某些Java型別轉換(casting)上的操作:
eg:
List<Apple> box = new ArrayList<Apple>();
box.add(new Apple());
Apple apple =box.get(0);
分析:
box是一個裝有Apple物件的List。get方法返回一個Apple物件例項,這個過程不需要進行型別轉換。沒有泛型,上面的程式碼需要寫成這樣:
Apple apple = (Apple)box.get(0);
2.泛型的尷尬
泛型的最大優點是提供了程式的型別安全同時可以向後相容,但尷尬的是每次定義時都要寫明泛型的型別,這樣在顯示指定上不僅感覺有些冗長,最主要是很多程式設計師不熟悉泛型,因此很多時候不能夠給出正確的型別引數,現在通過編譯器自動推斷泛型的引數型別,能夠減少這樣的情況,並提高程式碼可讀性。
3.java7的泛型型別推斷改進
在以前的版本中使用泛型型別,需要在宣告並賦值的時候,兩側都加上泛型型別。
eg:
Map<String, String> myMap = new HashMap<String, String>();
你可能覺得:在宣告變數的的時候已經指明瞭引數型別,為什麼還要在初始化物件時再指定?
幸好,在Java SE 7中,這種方式得以改進,現在你可以使用如下語句進行宣告並賦值:
Map<String, String> myMap = new HashMap<>(); //注意後面的"<>"
在這條語句中,編譯器會根據變數宣告時的泛型型別自動推斷出例項化HashMap時的泛型型別。再次提醒一定要注意new HashMap後面的“<>”,只有加上這個“<>”才表示是自動型別推斷,否則就是非泛型型別的HashMap,並且在使用編譯器編譯原始碼時會給出一個警告提示。
但是:Java SE 7在建立泛型例項時的型別推斷是有限制的;只有構造器的引數化型別在上下文中被顯著的聲明瞭,才可以使用型別推斷,否則不行。eg:下面的例子在java 7無法正確編譯(但現在在java8裡面可以編譯,因為根據方法引數來自動推斷泛型的型別):
List<String> list = new ArrayList<>(); list.add("A");// 由於addAll期望獲得Collection<? extends String>型別的引數,因此下面的語句無法通過 list.addAll(new ArrayList<>());
4.Java8的泛型目標型別推斷改進
java8裡面泛型的目標型別推斷主要有2個方面:
1)支援通過方法上下文推斷泛型目標型別
2)支援在方法呼叫鏈路當中,泛型型別推斷傳遞到最後一個方法官網的例子:
在呼叫上面方法的時候可以這樣寫:class List<E> { static <Z> List<Z> nil() { ... }; static <Z> List<Z> cons(Z head, List<Z> tail) { ... }; E head() { ... } }
//通過方法賦值的目標引數來自動推斷泛型的型別 List<String> l = List.nil(); //而不是顯示的指定型別 //List<String> l = List.<String>nil(); //通過前面方法引數型別推斷泛型的型別 List.cons(42, List.nil()); //而不是顯示的指定型別 //List.cons(42, List.<Integer>nil());
5.總結
Java作為靜態語言的代表者,可以說型別系統相當豐富。導致型別間互相轉換的問題困擾著每個java程式設計師,通過編譯器自動推斷型別的東西可以稍微緩解一下型別轉換太複雜的問題。雖然說是小進步,但對於我們天天寫程式碼的程式設計師,肯定能帶來巨大的作用。
6.諾諾個人理解:
其實泛型目標型別推斷改進主要是在使用Lambda表示式時用來推斷合法的Lambda表示式的型別的上下文 。