成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

高效開發!Lambda表達式和函數式接口最佳實踐

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

環境:Spring Boot 3.2.5

1. 簡介

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

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

接下來,我們將詳細研究函 數式接口 和 lambda 表達式。

2. 最佳實踐

2.1 優先使用標準的函數式接口

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

我們先來看下如下接口:

@FunctionalInterface
public interface Foo {


  String xxxooo(String string) ;
}

實用該接口:

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

運行程序應該是如下方式:

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

在這里的Foo接口方法簽名需要一個入參然后返回一個參數。而Java 8 已經在 java.util.function 包中提供了這樣的接口 Function<T, R>。所以我們沒有必要自己在定義,可以將上面的UseFoo修改如下:

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

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

2.2 使用@FunctionalInterface注解

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

然而,如果在一個大型項目中有多個接口;手動控制所有接口是很困難的。一個原本設計為函數式的接口可能會因為不小心添加了另一個抽象方法而被改變,從而使它不再是一個有效的函數式接口。

通過使用 @FunctionalInterface 注解,編譯器會在任何試圖破壞函數式接口預定義結構的行為時觸發錯誤。如下示例:

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

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

2.3 不要在函數式接口中過渡使用默認方法

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

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

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

@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")) ;
}

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

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

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

這樣定義后,Zoo接口將編譯不通過,重復的默認方法錯誤。

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

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

所以,我們不應該在函數式接口中定義過多的默認方法。

2.4 使用 Lambda 表達式實例化功能接口

編譯器允許我們使用內部類來實例化函數式接口;然而,這樣做會導致代碼非常冗長。我們應該優先使用 lambda 表達式:

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

如果是內部類定義那就太不優雅了。

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

現在開發工具都能自動幫你將這里的內部類轉換為lambda表達式。

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

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表達傳參時,就出問題了:

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

Eclipse下提示

圖片圖片

模棱兩可的方法調用。解決辦法有2種:

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

但是不推薦這種方式。

2.6 不要將 Lambda 表達式視為內部類

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

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

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

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) ;
}

輸出結果:

Inner class value
Outer class value

根據運行結果得知,在Lambda中this.value方法的是類中定義的變量,而內部類訪問的則是當前內部類的變量。

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

Lambda 表達式應該用一行代碼來編寫。通過這種方式,Lambda 表達式成為一個自解釋的結構,聲明了應該對哪些數據執行什么操作。

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

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

而不應該是如下代碼

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

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

2.8 避免指定參數類型

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

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

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

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

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

2.9 單參數不要使用括號

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

錯誤示例

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

正確示例

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

只有一個參數時沒有必要添加括號

2.10 避免返回語句和括號

理想情況下,Lambda 表達式應該用一行代碼來編寫。通過這種方式,Lambda 表達式成為一個自解釋的結構,聲明了應該對哪些數據執行什么操作。

錯誤示例

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

正確示例

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

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

2.11 方法引用

很多時候,即使在我們之前的示例中,lambda 表達式也只是調用其他地方已經實現的方法。 在這種情況下,使用 Java 8 的另一個特性--方法引用就非常有用了。

錯誤示例

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

正確示例

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

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

2.12 使用"Effectively Final"變量

在 Lambda 表達式內部訪問 非final 變量會導致編譯時錯誤,但這并不意味著我們應該將每個目標變量都標記為 final。根據 "Effectively final" 的概念,只要變量僅被賦值一次,編譯器就會將其視為 final。

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

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

圖片 圖片

責任編輯:武曉燕 來源: Spring全家桶實戰案例源碼
相關推薦

2022-12-05 09:31:51

接口lambda表達式

2024-03-08 09:45:21

Lambda表達式Stream

2020-10-16 10:07:03

Lambda表達式Java8

2022-12-01 07:38:49

lambda表達式函數式

2009-08-10 10:06:10

.NET Lambda

2009-08-31 17:11:37

Lambda表達式

2009-09-17 09:09:50

Lambda表達式Linq查詢

2020-10-16 06:40:25

C++匿名函數

2021-08-31 07:19:41

Lambda表達式C#

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擴展方Lambda表達式

2009-10-12 10:11:08

Lambda表達式編寫

2024-03-12 08:23:54

JavaLambda函數式編程

2025-04-01 08:12:10

2012-06-26 10:03:58

JavaJava 8lambda

2009-08-27 09:44:59

C# Lambda表達

2009-09-15 17:30:00

Linq Lambda

2009-09-17 09:44:54

Linq Lambda
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产亚洲精品成人av久久ww | 美女久久视频 | 亚洲欧美日本在线 | 欧美乱淫视频 | 久久精品国产一区二区电影 | 精品久久久久久久久久 | 欧美一区二区 | 精品国产一区久久 | 欧美电影在线观看网站 | 日韩精品在线一区 | 中国免费黄色片 | 国产最新视频在线 | 国产精品久久 | 国产亚洲日本精品 | 精品国产免费一区二区三区五区 | 色综合久久天天综合网 | 99re在线| 91久久国产综合久久 | ww 255hh 在线观看 | 亚洲精品欧洲 | 亚洲欧美bt | 九九亚洲| 中文字幕成人 | 涩涩操 | 久久久精品一区 | 国产精品一区二区在线观看 | 日日夜夜天天 | 天天操天天干天天爽 | 国产在线精品一区二区三区 | 久久久免费 | 亚洲在线一区 | 国产1区2区在线观看 | 久久国内精品 | 久久精品av | 免费av直接看 | 国产精品国产三级国产播12软件 | 一区二区在线免费观看 | av大片在线 | 国产福利小视频 | 91精品国产自产精品男人的天堂 | 欧美三级视频 |