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

高效開發(fā)!Lambda表達(dá)式和函數(shù)式接口最佳實(shí)踐

開發(fā) 前端
在 java.util.function 包中定義的函數(shù)式接口滿足了大多數(shù)開發(fā)者為 lambda 表達(dá)式和方法引用提供目標(biāo)類型的需求。這些接口中的每一個都是通用且抽象的,這使得它們能夠輕松適應(yīng)幾乎任何 lambda 表達(dá)式。

環(huán)境:Spring Boot 3.2.5

1. 簡介

Lambda表達(dá)式與函數(shù)式接口是Java 8引入的重要特性,它們極大地簡化了代碼編寫,提升了代碼的可讀性和簡潔性。Lambda表達(dá)式提供了一種簡潔的方式來表示匿名函數(shù),而函數(shù)式接口則是一種只包含一個抽象方法的接口,非常適合與Lambda表達(dá)式結(jié)合使用。

在實(shí)際開發(fā)中,合理運(yùn)用這些特性可以提高代碼的靈活性和復(fù)用性。最佳實(shí)踐中,推薦首先定義清晰且具有描述性的函數(shù)式接口,以增強(qiáng)代碼的可理解性;其次,在使用Lambda表達(dá)式時,盡量保持其簡短且功能單一,避免復(fù)雜的邏輯嵌套;此外,利用Stream API等函數(shù)式編程工具進(jìn)行集合操作,能夠使代碼更加流暢、高效。通過遵循這些原則,開發(fā)者不僅能夠?qū)懗龈觾?yōu)雅的代碼,還能更好地應(yīng)對并發(fā)編程的需求,提升程序的整體性能。

接下來,我們將詳細(xì)研究函 數(shù)式接口 和 lambda 表達(dá)式。

2. 最佳實(shí)踐

2.1 優(yōu)先使用標(biāo)準(zhǔn)的函數(shù)式接口

在 java.util.function 包中定義的函數(shù)式接口滿足了大多數(shù)開發(fā)者為 lambda 表達(dá)式和方法引用提供目標(biāo)類型的需求。這些接口中的每一個都是通用且抽象的,這使得它們能夠輕松適應(yīng)幾乎任何 lambda 表達(dá)式。我們在創(chuàng)建自定義的函數(shù)式接口之前,應(yīng)該優(yōu)先查看該包中的定義。

我們先來看下如下接口:

@FunctionalInterface
public interface Foo {


  String xxxooo(String string) ;
}

實(shí)用該接口:

public class UseFoo {
  public String pack(String param, Foo foo) {
    return foo.xxxooo(param) ;
  }
}

運(yùn)行程序應(yīng)該是如下方式:

Foo foo = param -> String.format("%s other info", param) ;
String ret = new UseFoo().pack("Message ", foo) ;

在這里的Foo接口方法簽名需要一個入?yún)⑷缓蠓祷匾粋€參數(shù)。而Java 8 已經(jīng)在 java.util.function 包中提供了這樣的接口 Function<T, R>。所以我們沒有必要自己在定義,可以將上面的UseFoo修改如下:

public String pack(String param, Function<String, String> foo) {
  return foo.apply(param) ;
}
// 調(diào)用
Function<String, String> foo = param -> String.format("%s other info", param) ;

使用與之前定義的基本相同。

2.2 使用@FunctionalInterface注解

使用 @FunctionalInterface 注解接口。乍一看,這個注解似乎沒有什么用處。即使沒有它,只要接口中只有一個抽象方法,該接口也會被視為函數(shù)式接口。

然而,如果在一個大型項(xiàng)目中有多個接口;手動控制所有接口是很困難的。一個原本設(shè)計(jì)為函數(shù)式的接口可能會因?yàn)椴恍⌒奶砑恿肆硪粋€抽象方法而被改變,從而使它不再是一個有效的函數(shù)式接口。

通過使用 @FunctionalInterface 注解,編譯器會在任何試圖破壞函數(shù)式接口預(yù)定義結(jié)構(gòu)的行為時觸發(fā)錯誤。如下示例:

@FunctionalInterface
public interface Foo {
  String xxxooo(String param) ;
}

使用該接口,能防止你再定義其它方法。

2.3 不要在函數(shù)式接口中過渡使用默認(rèn)方法

我們可以輕松地在函數(shù)式接口中添加默認(rèn)方法。只要接口中只有一個抽象方法聲明,這樣做是符合函數(shù)式接口契約的:

@FunctionalInterface
public interface Foo {
  String xxxooo(String param);
  default void defaultMethod() {
    // ...
  }
}

如果它們的抽象方法具有相同的簽名,函數(shù)式接口可以被其他函數(shù)式接口繼承:

@FunctionalInterface
public interface Zoo extends Baz, Bar {}
  
@FunctionalInterface
public interface Baz {  
  String xxxooo(String param);  
  default String defaultBaz() {
    return "Baz..." ;
  }    
}
  
@FunctionalInterface
public interface Bar {  
  String xxxooo(String param);  
  default String defaultBar() {
    return "Bar..." ;
  }  
}
public static void main(String[] args) {
  Zoo zoo = param -> String.format("%s extends", param) ;
  System.out.println(zoo.xxxooo("Functional Interface")) ;
}

就像普通的接口一樣,如果不同的函數(shù)式接口繼承了具有相同默認(rèn)方法的接口,這也可能會帶來問題。

修改上面的Baz和Bar接口,添加相同的默認(rèn)方法:

@FunctionalInterface
public interface Baz { 
  default String print(){
    // ...
  }
}
@FunctionalInterface
public interface Bar { 
  default String print(){
    // ...
  }
}

這樣定義后,Zoo接口將編譯不通過,重復(fù)的默認(rèn)方法錯誤。

我們可以通過如下方式,在Zoo接口重寫defaultCommon方法,如下示例:

@FunctionalInterface
public interface Zoo extends Baz, Bar {
  @Override
  default String print() {
    return Bar.super.print() ;
  }
}

所以,我們不應(yīng)該在函數(shù)式接口中定義過多的默認(rèn)方法。

2.4 使用 Lambda 表達(dá)式實(shí)例化功能接口

編譯器允許我們使用內(nèi)部類來實(shí)例化函數(shù)式接口;然而,這樣做會導(dǎo)致代碼非常冗長。我們應(yīng)該優(yōu)先使用 lambda 表達(dá)式:

// 是使用上面定義的Zoo接口
Zoo zoo = param -> String.format("%s extends", param) ;
System.out.println(zoo.xxxooo("Functional Interface")) ;

如果是內(nèi)部類定義那就太不優(yōu)雅了。

Zoo zoo = new Zoo() {
  public String xxxooo(String param) {
    return String.format("%s extends", param) ;
  }
} ;

現(xiàn)在開發(fā)工具都能自動幫你將這里的內(nèi)部類轉(zhuǎn)換為lambda表達(dá)式。

2.5 避免重載帶有函數(shù)式接口作為參數(shù)的方法

public interface Processor {
  String process(Callable<String> c) throws Exception;


  String process(Supplier<String> s);
}


public class ProcessorImpl implements Processor {
  public String process(Callable<String> c) throws Exception {
    return c.call() ;
  }
  public String process(Supplier<String> s) {
    return s.get() ;
  }
}

上面代碼看著沒撒毛病,但是你通過lambda表達(dá)傳參時,就出問題了:

ProcessorImpl process = new ProcessorImpl() ;
process.process(() -> "Pack") ;

Eclipse下提示

圖片圖片

模棱兩可的方法調(diào)用。解決辦法有2種:

  • 定義不同的方法名稱
  • 強(qiáng)制轉(zhuǎn)換
ProcessorImpl process = new ProcessorImpl() ;
process.process((Supplier<String>)() -> "Pack") ;

但是不推薦這種方式。

2.6 不要將 Lambda 表達(dá)式視為內(nèi)部類

盡管在前面的例子中,我們基本上是用 Lambda 表達(dá)式替換了內(nèi)部類,但這兩個概念在一個重要方面是不同的:作用域。

當(dāng)我們使用內(nèi)部類時,它會創(chuàng)建一個新的作用域。我們可以通過實(shí)例化具有相同名稱的新局部變量來隱藏外部作用域中的局部變量。我們還可以在內(nèi)部類中使用 this 關(guān)鍵字作為對其自身實(shí)例的引用。

然而,Lambda 表達(dá)式則與外部作用域一起工作。我們不能在 Lambda 表達(dá)式的主體中隱藏外部作用域中的變量。在這種情況下,this 關(guān)鍵字是對外部實(shí)例的引用。

private String value = "Outer class value";


@FunctionalInterface
public interface Foo {
  String fn(String param);
}


public void xxoo() {
  Foo f = new Foo() {
    String value = "Inner class value";


    @Override
    public String fn(String param) {
      return this.value;
    }
  };
  String ret = f.fn("Pack") ;
  System.out.println(ret) ;
  Foo fl = param -> {
    String value = "Lambda value";
    return this.value;
  };
  ret = fl.fn("Pack");


  System.out.println(ret) ;
}

輸出結(jié)果:

Inner class value
Outer class value

根據(jù)運(yùn)行結(jié)果得知,在Lambda中this.value方法的是類中定義的變量,而內(nèi)部類訪問的則是當(dāng)前內(nèi)部類的變量。

2.7 避免在 Lambda 表達(dá)式的主體中使用代碼塊

Lambda 表達(dá)式應(yīng)該用一行代碼來編寫。通過這種方式,Lambda 表達(dá)式成為一個自解釋的結(jié)構(gòu),聲明了應(yīng)該對哪些數(shù)據(jù)執(zhí)行什么操作。

如果我們有一大段代碼,那么 lambda 的功能就不會立即顯現(xiàn)出來。

Foo foo = param -> buildString(param) ;
private String buildString(String param) {
  String result = "Something " + param ;
  // ...
  return result ;
}

而不應(yīng)該是如下代碼

Foo foo = param -> { 
  String result = "Something " + param ; 
  // ...
  return result ; 
} ;

注意:如果 lambda的定義有兩三行代碼,那么將代碼提取到另一個方法中也沒有什么價值,我們不應(yīng)該將 "單行 lambda" 完全作為一個規(guī)約。

2.8 避免指定參數(shù)類型

在大多數(shù)情況下,編譯器可以通過類型推斷來確定 lambda 參數(shù)的類型。 因此,為參數(shù)添加類型是可選的,可以省略,如下實(shí)例:

BiFunction<String, String, String> fun = 
  (String a, String b) -> a.toLowerCase() + b.toLowerCase() ;

這里我們不用聲明類型,而是如下方式:

BiFunction<String, String, String> fun = 
  (a, b) -> a.toLowerCase() + b.toLowerCase() ;

這里完全可以通過類型推斷確定類型,所以沒有必要什么參數(shù)的類型。

2.9 單參數(shù)不要使用括號

Lambda 語法只要求在多個參數(shù)或沒有參數(shù)時使用括號。

錯誤示例

Function<String, String> fun = (a) -> a.toLowerCase() ;

正確示例

Function<String, String> fun = a -> a.toLowerCase() ;

只有一個參數(shù)時沒有必要添加括號

2.10 避免返回語句和括號

理想情況下,Lambda 表達(dá)式應(yīng)該用一行代碼來編寫。通過這種方式,Lambda 表達(dá)式成為一個自解釋的結(jié)構(gòu),聲明了應(yīng)該對哪些數(shù)據(jù)執(zhí)行什么操作。

錯誤示例

Function<String, String> func = a -> {return a.toLowerCase()};

正確示例

Function<String, String> func = a -> a.toLowerCase() ;

這里我們沒有必要使用代碼塊,我們應(yīng)該時刻注意盡可能的使得 Lambda 表達(dá)式只有一行。

2.11 方法引用

很多時候,即使在我們之前的示例中,lambda 表達(dá)式也只是調(diào)用其他地方已經(jīng)實(shí)現(xiàn)的方法。 在這種情況下,使用 Java 8 的另一個特性--方法引用就非常有用了。

錯誤示例

Function<String, String> func = a -> a.toLowerCase();

正確示例

Function<String, String> func = String::toLowerCase;

如果你不懂方法引用,那么這種寫法是不是可讀性不好了?

2.12 使用"Effectively Final"變量

在 Lambda 表達(dá)式內(nèi)部訪問 非final 變量會導(dǎo)致編譯時錯誤,但這并不意味著我們應(yīng)該將每個目標(biāo)變量都標(biāo)記為 final。根據(jù) "Effectively final" 的概念,只要變量僅被賦值一次,編譯器就會將其視為 final。

public void xxxooo() {
  String value = "Local" ; // 這里我們可以省去 final 修飾符
  Function<String, String> func = str -> {
    return value ;
  } ;
}

這里我們沒有必要在變量value前使用 final 修飾。但是我們不能在代碼塊中去修改,如下將無法編譯通過:

圖片 圖片

責(zé)任編輯:武曉燕 來源: Spring全家桶實(shí)戰(zhàn)案例源碼
相關(guān)推薦

2022-12-05 09:31:51

接口lambda表達(dá)式

2024-03-08 09:45:21

Lambda表達(dá)式Stream

2020-10-16 10:07:03

Lambda表達(dá)式Java8

2022-12-01 07:38:49

lambda表達(dá)式函數(shù)式

2009-08-10 10:06:10

.NET Lambda

2009-08-31 17:11:37

Lambda表達(dá)式

2009-09-17 09:09:50

Lambda表達(dá)式Linq查詢

2021-08-31 07:19:41

Lambda表達(dá)式C#

2020-10-16 06:40:25

C++匿名函數(shù)

2009-09-11 09:48:27

Linq Lambda

2009-09-09 13:01:33

LINQ Lambda

2009-09-15 15:18:00

Linq Lambda

2009-08-10 17:11:34

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

2009-10-12 10:11:08

Lambda表達(dá)式編寫

2024-03-12 08:23:54

JavaLambda函數(shù)式編程

2025-04-01 08:12:10

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
點(diǎn)贊
收藏

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