日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java 8 - 04 类型检查、类型推断以及限制

發(fā)布時間:2025/3/21 java 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java 8 - 04 类型检查、类型推断以及限制 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • Pre
  • 類型檢查
  • 同樣的 Lambda,不同的函數(shù)式接口
    • 菱形運算符
    • 特殊的void兼容規(guī)則
  • 類型推斷
  • 使用局部變量

Pre

當(dāng)我們第一次提到Lambda表達(dá)式時,說它可以為函數(shù)式接口生成一個實例。然而,Lambda
表達(dá)式本身并不包含它在實現(xiàn)哪個函數(shù)式接口的信息。為了全面了解Lambda表達(dá)式,women 應(yīng)該知道Lambda的實際類型是什么 .


類型檢查

Lambda的類型是從使用Lambda的上下文推斷出來的。 上下文(比如,接受它傳遞的方法的參數(shù),或接受它的值的局部變量)中Lambda表達(dá)式需要的類型稱為目標(biāo)類型。

舉個例子

List<Apple> heavierThan150g = filter(inventory, (Apple a) -> a.getWeight() > 150);

類型檢查過程可以分解為如下所示。
? 首先,我們要找出 filter 方法的聲明。
? 第二,要求它是 Predicate<Apple> (目標(biāo)類型)對象的第二個正式參數(shù)。
? 第三, Predicate<Apple> 是一個函數(shù)式接口,定義了一個叫作 test 的抽象方法。
? 第四, test 方法描述了一個函數(shù)描述符,它可以接受一個 Apple ,并返回一個 boolean 。
? 最后, filter 的任何實際參數(shù)都必須匹配這個要求

這段代碼是有效的,因為我們所傳遞的Lambda表達(dá)式也同樣接受 Apple 為參數(shù),并返回一個boolean 。請注意,如果Lambda表達(dá)式拋出一個異常,那么抽象方法所聲明的 throws 語句也必須與之匹配


同樣的 Lambda,不同的函數(shù)式接口

有了目標(biāo)類型的概念,同一個Lambda表達(dá)式就可以與不同的函數(shù)式接口聯(lián)系起來,只要它們的抽象方法簽名能夠兼容.

我們來看下這兩個函數(shù)式接口


這兩個函數(shù)式接口 都是 什么也不接受且返回一個泛型 T 的函數(shù), 所以 下面兩個賦值是有效的

Callable<Integer> integerCallable = () -> 18;PrivilegedAction<Integer> privilegedAction = () -> 18;

第一個賦值的目標(biāo)類型是 Callable<Integer>
第二個賦值的目標(biāo)類型是PrivilegedAction<Integer>

再舉個栗子 : 同一個Lambda可用于多個不同的函數(shù)式接口

Comparator<Enginner> enginnerComparator = (e1, e2) -> e1.getJob().compareTo(e2.getJob());ToIntBiFunction<Enginner, Enginner> toIntBiFunction = (e1, e2) -> e1.getJob().compareTo(e2.getJob());BiFunction<Enginner, Enginner, Integer> toIntFunction = (e1, e2) -> e1.getJob().compareTo(e2.getJob());

Comparator 、 ToIntBiFunction 、 BiFunction 都是返回一個int類型的的函數(shù)


菱形運算符

Java 7中已經(jīng)引入了菱形運算符( <> ),利用泛型推斷從上下文推斷類型的思想。 一個類實例表達(dá)式可以出現(xiàn)在兩個或更多不同的上下文中,并會像下面這樣推斷出適當(dāng)?shù)念愋蛥?shù)。

List<String> listOfStrings = new ArrayList<>(); List<Integer> listOfIntegers = new ArrayList<>();

特殊的void兼容規(guī)則

如果一個Lambda的主體是一個語句表達(dá)式, 它就和一個返回 void 的函數(shù)描述符兼容(當(dāng)然需要參數(shù)列表也兼容)。

舉個例子:

以下兩行都是合法的,盡管 List 的 add 方法返回了一個boolean ,而不是 Consumer 上下文( T -> void )所要求的 void

List<String> stringList = new ArrayList<>();// Predicate返回了一個booleanPredicate<String> predicate = s -> stringList.add(s);// Consumer返回了一個voidConsumer<String> consumer = s -> stringList.add(s);

經(jīng)過了這幾個小demo ,是不是能夠很好地理解在什么時候以及在哪里可以使用Lambda表達(dá)式了。Lambda表達(dá)式可以從賦值的上下文、方法調(diào)用的上下文(參數(shù)和返回值),以及類型轉(zhuǎn)換的上下文中獲得目標(biāo)類型

來個小測驗

類型檢查——為什么下面的代碼不能編譯呢? Object o = () -> {System.out.println("Tricky example"); };答案: Lambda表達(dá)式的上下文是 Object (目標(biāo)類型)。但 Object 不是一個函數(shù)式接口 。 為了解決這個問題,可以把目標(biāo)類型改成 Runnable ,它的函數(shù)描述符是 () -> void :Runnable r = () -> {System.out.println("Tricky example"); };

類型推斷

剛才已經(jīng)討論了如何利用目標(biāo)類型來檢查一個Lambda是否可以用于某個特定的上下文。其實,
它也可以用來做一些略有不同的事:推斷Lambda參數(shù)的類型,我們來看下。

Java編譯器會從上下文(目標(biāo)類型)推斷出用什么函數(shù)式接口來配合Lambda表達(dá)式,這意味著它也可以推斷出適合Lambda的簽名,因為函數(shù)描述符可以通過目標(biāo)類型來得到。這樣做的好處在于,編譯器可以了解Lambda表達(dá)式的參數(shù)類型,這樣就可以在Lambda語法中省去標(biāo)注參數(shù)類型.

舉個例子

List<Enginner> goEngineerList = filter(enginnerList,a-> a.getJob().equals("GO"));

參數(shù) a 沒有顯式類型 .

再舉個栗子 ,Lambda表達(dá)式有多個參數(shù),代碼可讀性的好處就更為明顯

// 沒有類型推斷,因為給o1,o2指定了Enginner 類型Comparator<Enginner> comparator = (Enginner o1, Enginner o2) -> o1.getJob().compareTo(o2.getJob());// 有類型推斷,因為沒有給o1,o2指定了Enginner 類型Comparator<Enginner> comparator2 = ( o1, o2) -> o1.getJob().compareTo(o2.getJob());

個人感覺,第二種寫法更簡單 。

當(dāng)Lambda僅有一個類型需要推斷的參數(shù)時,參數(shù)名稱兩邊的括號也可以省略。


使用局部變量

上面所介紹的所有Lambda表達(dá)式都只用到了其主體里面的參數(shù)。但Lambda表達(dá)式也允許使用自由變量(不是參數(shù),而是在外層作用域中定義的變量),就像匿名類一樣。 它們被稱作捕獲Lambda。

舉個例子

int num = 1;Runnable runnable = ()->System.out.println(num);

這么做雖然有點啰嗦,我們這里想要討論的是 使用外部的變量有什么限制嗎?

如果你想要對這個變量進(jìn)行操作,之前的lambda就報錯了。所以說Lambda可以沒有限制地捕獲(也就是在其主體中引用)實例變量和靜態(tài)變量,但是局部變量必須顯式聲明為 final.

換句話說,Lambda表達(dá)式只能捕獲指派給它們的局部變量一次。(注:捕獲實例變量可以被看作捕獲最終局部變量 this 。) 如上圖。

為什么會這樣呢?

  • 第一: 實例變量都存儲在堆中,而局部變量則保存在棧上。如果Lambda可以直接訪問局部變量,而且Lambda是在一個線程中使用的,則使用Lambda的線程,可能會在分配該變量的線程將這個變量收回之后,去訪問該變量。因此,Java在訪問自由局部變量時,實際上是在訪問的副本,而不是訪問原始變量。如果局部變量僅僅賦值一次那就沒有什么區(qū)別了——因此就有了這個限制
  • 第二,這一限制不鼓勵你使用改變外部變量的典型命令式編程模式,這種模式會阻礙很容易做到的并行處理.

總結(jié)

以上是生活随笔為你收集整理的Java 8 - 04 类型检查、类型推断以及限制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。