自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

「lambda表達(dá)式」函數(shù)式接口、方法引用與構(gòu)造器引用

開(kāi)發(fā) 前端
Java 有一個(gè)限制,無(wú)法構(gòu)造泛型類型 T 的數(shù)組。數(shù)組構(gòu)造器引用對(duì)于克服這個(gè)限制很有用。表達(dá)式 new T[n] 會(huì)產(chǎn)生錯(cuò)誤,因?yàn)檫@會(huì)改為 new Object[n] 。

函數(shù)式接口

Java 中已經(jīng)有很多封裝代碼塊的接口,如 ActionListener 或 Comparator。 lambda 表達(dá)式與這些接口是兼容的。對(duì)于只有一個(gè)抽象方法的接口, 需要這種接口的對(duì)象時(shí), 就可以提供一個(gè) lambda 表達(dá)式。這種接口稱為函數(shù)式接口( functional interface ) 。

為什么函數(shù)式接口必須有一個(gè)抽象方法。不是接口中的所有方法都是抽象的嗎? 實(shí)際上,接口完全有可能重新聲明 Object 類的方法, 如 toString 或 clone, 這些聲明有可能會(huì)讓方法不再是抽象的。( Java API 中的一些接口會(huì)重新聲明 Object 方法 來(lái)附加 javadoc 注釋。Comparator API 就是這樣一個(gè)例子。)更重要的是, 正如 6.1.5 節(jié)所述, 在 JavaSE 8 中, 接口可以聲明非抽象方法。

最好把 lambda 表達(dá)式看作是一 個(gè)函數(shù),而不是一個(gè)對(duì)象, 另外要接受 lambda 表達(dá)式可以傳遞到函數(shù)式接口。 lambda 表達(dá)式可以轉(zhuǎn)換為接口, 這一點(diǎn)讓 lambda 表達(dá)式很有吸引力。具體的語(yǔ)法很簡(jiǎn)短。

實(shí)際上,在 Java 中, 對(duì) lambda 表達(dá)式所能做的也只是能轉(zhuǎn)換為函數(shù)式接口。在其他支持函數(shù)字面量的程序設(shè)計(jì)語(yǔ)言中,可以聲明函數(shù)類型(如(String, String) -> int) 、 聲明這些類型的變量,還可以使用變量保存函數(shù)表達(dá)式。不過(guò),Java 設(shè)計(jì)者還是決定保持我們熟悉的接口概念, 沒(méi)有為 Java 語(yǔ)言增加函數(shù)類型。

甚至不能把 lambda 表達(dá)式賦值給類型為 Object 的變量,Object 不是一個(gè)函數(shù)式接口。

Java API 在 java.util.function 包中定義了很多非常通用的函數(shù)式接口。其中一個(gè)接口 BiFunction<T,U,R> 描述了參數(shù)類型為 T 和 U 而且返回類型為 R 的函數(shù)??梢园盐覀兊淖址容^ lambda 表達(dá)式保存在這個(gè)類型的變量中:

BiFunction<String,String,Integer> comp = (first,second) -> first.length() - second.length();

類似 Comparator 的接口往往有一個(gè)特定的用途, 而不只是提供一個(gè)有指定參數(shù)和返回類型的方法。Java SE 8 沿襲了這種思路。想要用 lambda 表達(dá)式做某些處理,還是要謹(jǐn)記表達(dá)式的用途,為它建立一個(gè)特定的函數(shù)式接口。

java.util.function 包中有一個(gè)尤其有用的接口 Predicate:

public interface Predicate<T>
{
boolean test(T t);
// Additional default and static methods
}

ArrayList 類有一個(gè) removelf 方法, 它的參數(shù)就是一個(gè) Predicate 。這個(gè)接口專門用來(lái)傳遞 lambda 表達(dá)式。例如,下面的語(yǔ)句將從一個(gè)數(shù)組列表刪除所有 null 值: list.removelf(e -> e == null);

方法引用

有時(shí), 可能已經(jīng)有現(xiàn)成的方法可以完成你想要傳遞到其他代碼的某個(gè)動(dòng)作。例如,假設(shè)你希望只要出現(xiàn)一個(gè)定時(shí)器事件就打印這個(gè)事件對(duì)象。 當(dāng)然,為此也可以調(diào)用:

Timer t = new Timer(1000, event -> System.out .println(event)):

但是,如果直接把 println 方法傳遞到 Timer 構(gòu)造器就更好了。具體做法如下:

Timer t = new Timer(1000, System.out::println);

表達(dá)式 System.out::println 是一個(gè)方法引用( method reference ), 它等價(jià)于 lambda 表達(dá)式

x -> System.out.println(x) 。

再來(lái)看一個(gè)例子,假設(shè)你想對(duì)字符串排序,而不考慮字母的大小寫??梢詡鬟f以下方法表達(dá)式:

Arrays.sort(strings , String::compareToIgnoreCase)

從這些例子可以看出, 要用 :: 操作符分隔方法名與對(duì)象或類名。主要有 3 種情況:

  • object::instanceMethod
  • Class::staticMethod
  • Class::instanceMethod

在前 2 種情況中,方法引用等價(jià)于提供方法參數(shù)的 lambda 表達(dá)式。前面已經(jīng)提到的,System.out::println等價(jià)于 x -> System.out.println(x) 。類似地,Math::pow 等價(jià)于(x , y) -> Math.pow(x , y)。

對(duì)于第 3 種情況, 第 1 個(gè)參數(shù)會(huì)成為方法的目標(biāo)。例如,
String::compareToIgnoreCase 等同于 (x, y) -> x.compareToIgnoreCase(y) 。

如果有多個(gè)同名的重栽方法, 編譯器就會(huì)嘗試從上下文中找出你指的那一個(gè)方法。 例如, Math.max 方法有兩個(gè)版本, 一個(gè)用于整數(shù), 另一個(gè)用于 double 值。選擇哪一個(gè)版本取決于 Math::max 轉(zhuǎn)換為哪個(gè)函數(shù)式接口的方法參數(shù)。 類似于 lambda 表達(dá)式,方法引用不能獨(dú)立存在,總是會(huì)轉(zhuǎn)換為函數(shù)式接口的實(shí)例。

可以在方法引用中使用 this 參數(shù)。例如,this::equals 等同于 x -> this.equals(x) 。 使用 super 也是合法的。 下面的方法表達(dá)式:

super::instanceMethod

使用 this 作為目標(biāo),會(huì)調(diào)用給定方法的超類版本。

class Greeter{
public void greet(){
System.out.println("Hello, world!");
}
}
class TimedGreeter extends Greeter{
public void greet(){
Timer t = new Timer(1000, super::greet);
t.start();
}
}

TimedGreeter.greet 方法開(kāi)始執(zhí)行時(shí),會(huì)構(gòu)造一個(gè) Timer, 它會(huì)在每次定時(shí)器滴答時(shí)執(zhí)行 super::greet 方法。這個(gè)方法會(huì)調(diào)用超類的 greet 方法。

構(gòu)造器引用

構(gòu)造器引用與方法引用很類似,只不過(guò)方法名為 new 。例如,Person::new 是 Person 構(gòu)造器的一個(gè)引用。哪一個(gè)構(gòu)造器呢? 這取決于上下文。假設(shè)你有一個(gè)字符串列表??梢园阉D(zhuǎn)換為一個(gè) Person 對(duì)象數(shù)組,為此要在各個(gè)字符串上調(diào)用構(gòu)造器,調(diào)用如下:

ArrayList names = . . .; 
Stream stream = names.stream().map(Person::new);
List people = stream.collect(Collectors.toList());

map 方法會(huì)為各個(gè)列表元素調(diào)用 Person(String) 構(gòu)造器。如果有多個(gè) Person 構(gòu)造器, 編譯器會(huì)選擇有一個(gè) String 參數(shù)的構(gòu)造器, 因?yàn)樗鼜纳舷挛耐茖?dǎo)出這是在對(duì)一個(gè)字符串調(diào)用構(gòu)造器。

可以用數(shù)組類型建立構(gòu)造器引用。例如, int[]::new 是一個(gè)構(gòu)造器引用,它有一個(gè)參數(shù): 即數(shù)組的長(zhǎng)度。這等價(jià)于 lambda 表達(dá)式 x -> new int[x] ;

Java 有一個(gè)限制,無(wú)法構(gòu)造泛型類型 T 的數(shù)組。數(shù)組構(gòu)造器引用對(duì)于克服這個(gè)限制很有用。表達(dá)式 new T[n] 會(huì)產(chǎn)生錯(cuò)誤,因?yàn)檫@會(huì)改為 new Object[n] 。 對(duì)于開(kāi)發(fā)類庫(kù)的人來(lái)說(shuō),這是一個(gè)問(wèn)題。例如,假設(shè)我們需要一個(gè) Person 對(duì)象數(shù)組。Stream 接口有一個(gè) toArray 方法可以返回 Object 數(shù)組:

Object[] people = stream.toArray();

不過(guò),這并不讓人滿意。用戶希望得到一個(gè) Person 引用數(shù)組,而不是 Object 引用數(shù)組。 流庫(kù)利用構(gòu)造器引用解決了這個(gè)問(wèn)題。可以把 Person[]::new 傳入 toArray 方法:

Person[] people = stream.toArray(Person[]::new);

toArray 方法調(diào)用這個(gè)構(gòu)造器來(lái)得到一個(gè)正確類型的數(shù)組。然后填充這個(gè)數(shù)組并返回。

責(zé)任編輯:武曉燕 來(lái)源: 今日頭條
相關(guān)推薦

2022-12-05 09:31:51

接口lambda表達(dá)式

2024-12-02 10:56:29

2024-03-08 09:45:21

Lambda表達(dá)式Stream

2021-08-31 07:19:41

Lambda表達(dá)式C#

2009-08-10 10:06:10

.NET Lambda

2009-08-31 17:11:37

Lambda表達(dá)式

2020-10-16 10:07:03

Lambda表達(dá)式Java8

2024-03-25 13:46:12

C#Lambda編程

2020-10-16 06:40:25

C++匿名函數(shù)

2009-09-11 09:48:27

Linq Lambda

2009-08-10 17:11:34

.NET 3.5擴(kuò)展方Lambda表達(dá)式

2009-09-09 13:01:33

LINQ Lambda

2009-09-15 15:18:00

Linq Lambda

2021-06-08 07:48:26

lambda表達(dá)式編譯器

2009-10-12 10:11:08

Lambda表達(dá)式編寫

2012-06-26 10:03:58

JavaJava 8lambda

2009-08-27 09:44:59

C# Lambda表達(dá)

2009-09-17 10:40:22

Linq Lambda

2009-09-15 17:30:00

Linq Lambda

2009-09-17 09:44:54

Linq Lambda
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)