Java中的類型推斷和lambda表達式

簡介

java是強類型的編程語言,每個java中使用到的變量都需要定義它的類型,否則會編譯失敗。強類型語言的好處就是可以儘可能的在編譯期間就發現代碼中可能出現的問題,從而減少在運行時出現問題的可能性。

相對的,強類型語言的缺點就是不那麼的靈活多變,寫起來比較冗餘。

JDK8之前,java是不支持類型推斷的,在JDK8中,引入了lambda表達式,從此類型推斷產生了。

本文將會講解類型推斷在lambda表達式中的最佳實踐和在使用中應該注意的事項。

更多精彩內容且看:

更多內容請訪問www.flydean.com

類型的顯示使用

假如我們定義了一個CustUser類,並且其中有age和name兩個屬性:

@Data
@AllArgsConstructor
public class CustUser {
    int age;
    String name;
}

看下我們怎麼在Stream中顯示使用類型:

public static void testStream(){
        Stream.of(new CustUser(10,"alice"), new CustUser(20,"bluce"))
                .forEach( (CustUser custUser)-> System.out.println(custUser.name));
    }

上面的例子中,我們構建了一個CustUser類型的Stream,並在forEach方法中對CustUser進行處理。

forEach接收一個Consumer對象,Consumer需要實現void accept(T t)方法。因爲Consumer函數接口,我們可以使用lambda表達式來替換。

這裏,我們顯示傳入一個CustUser類型。代碼編譯是沒有問題的,但是看起來複雜了點。接下來我們看下怎麼在Stream中使用類型推斷。

Stream中的類型推斷

上面的例子,我們可以改寫成這樣:

public static void testInference(){
        Stream.of(new CustUser(10,"alice"), new CustUser(20,"bluce"))
                .forEach(custUser-> System.out.println(custUser.name));
    }

這裏我們並沒有定義custUser的類型,但是java可以從Stream中的類型推斷出來。所以這樣寫是沒有問題的,可以正常通過編譯。

類型推斷中變量名字的重要性

上面的例子中,我們將變量的名字定義爲custUser,查看代碼的人一眼就可以看出來這個參數表示的是CustUser類型的custUser參數。

名字寫的有意義可以很大程度上提升代碼的可讀性和可維護性。如果你這樣寫:

forEach(u-> System.out.println(u.name)

雖然代碼變得更短了,但是失去了可讀的意義,一眼看過去,大家並不知道你這個u代表的是什麼,從而影響了代碼的可讀性。

所以變量名的定義一定要有意義。

類型推斷對性能的影響

類型推斷是個好東西,那麼有同學會問了,類型推斷對於java的性能會有影響嗎?

我們可以把java分成編譯和運行兩部分。類型推斷是在編譯期間做的事情,可能使用類型推斷會延長代碼編譯的時間,但是對運行時的效率是沒有影響的。

一般來說,我們關注程序的性能問題是在運行時而不是編譯時,所以類型推斷對性能是沒有影響的。

類型推斷的限制

java雖然有類型推斷,但是這個推斷是有一定的限制的,它並不能夠像人一樣去思考,但是也已經足夠智能了。下面我們舉個例子:

public static Comparator<CustUser> createUser1(){
        return (CustUser user1, CustUser user2) -> user1.getAge() - user2.getAge();
    }

上面的例子中,我們需要創建一個Comparator,使用lambda表達式我們可以生成一個Comparator。

Comparator需要實現方法int compare(T o1, T o2),傳入兩個參數,返回一個int。

上面例子中,我們顯示指定了兩個參數的類型是CustUser,編譯沒有問題。

如果不顯示指定CustUser類型可以嗎?

public static Comparator<CustUser> createUser2(){
        return (user1, user2) -> user1.getAge() - user2.getAge();
    }

答案也是可以的。這個例子中,我們並沒有傳入user1,user2,java是怎麼找到user1和user2的類型呢?

注意,上面的例子中,我們定義了返回類型是CustUser的,Java通過這個返回類型來推斷出傳入的實際類型就是CustUser的。是不是很智能。

如果我們將上面的return語句拆分成兩條,會出現問題問題呢?

Comparator comparator=(user1, user2) -> user1.getAge() - user2.getAge();

這時候就會編譯報錯,說找不到getAge方法。這是因爲我們返回的Comparator並沒有指明類型,所以默認情況下是Object類型。Object類型並沒有getAge方法,所以報錯。

我們可以這樣改寫:

Comparator<CustUser> comparator=(user1, user2) -> user1.getAge() - user2.getAge();

編譯完成,沒有錯誤。

總結

除了JDK8中引入的lambda表示中使用了類型推斷,其實JDK10中的var本地變量類型也是用到了類型推斷,詳請參考JDK10的新特性:本地變量類型var

本文的例子https://github.com/ddean2009/
learn-java-base-9-to-20

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/java-type-inference-lambda/

本文來源:flydean的博客

歡迎關注我的公衆號:程序那些事,更多精彩等着您!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章