欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java8如何使用Lambda表達式簡化代碼詳解

 更新時間:2020年11月13日 11:05:06   作者:超級小豆丁  
這篇文章主要給大家介紹了關于Java8如何使用Lambda表達式簡化的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

系統(tǒng)環(huán)境:

  • Java JDK 版本:1.8

參考地址:

一、Lambda 表達式簡介

1、什么是 Lambda 表達式

       Lambda 表達式是在 JDK 8 中引入的一個新特性,可用于取代大部分的匿名內(nèi)部類。使用 Lambda 表達式可以完成用少量的代碼實現(xiàn)復雜的功能,極大的簡化代碼代碼量和代碼結(jié)構。同時,JDK 中也增加了大量的內(nèi)置函數(shù)式接口供我們使用,使得在使用 Lambda 表達式時更加簡單、高效。

2、為什么需要 Lambda 表達式

       談起為什么需要 Lambda 表達式,那得從函數(shù)式編程開始說起。函數(shù)式編程可以簡單說是一種編程規(guī)范,也就是如何編寫程序的方法論。它屬于結(jié)構化編程的一種,主要思想是把運算過程盡量寫成一系列嵌套的函數(shù)調(diào)用。函數(shù)式編程有很多優(yōu)點,其中包括:

  • 易于并發(fā)編程;
  • 代碼的熱升級;
  • 更方便的代碼管理;
  • 代碼簡潔,開發(fā)快速;
  • 接近自然語言,易于理解;

函數(shù)式編程在 C#、Python、JavaScript中都得到充分體現(xiàn),在 Java 8 版本中也得到了支持。最明顯的就是對 Lambda 表達式的支持。很多種跡象表明未來編程語言將是逐漸融合各自的特性,而不是單純的聲明式語言函數(shù)編程語言。將來聲明式編程語言借鑒函數(shù)編程思想,函數(shù)編程語言融合聲明式編程特性,這幾乎是一種必然趨勢。

       在 Java 中主要引入 Lambda 表達式的作用是對現(xiàn)有編碼語義的優(yōu)化,減少語法冗余。輕量級的將代碼封裝為數(shù)據(jù),使代碼簡潔,易于理解。

二、函數(shù)式接口和定義

1、什么是函數(shù)式接口

       函數(shù)式接口(Functional Interface)是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的接口。Java 中函數(shù)式接口被隱式轉(zhuǎn)換為 Lambda 表達式,只有保證接口類中有且只有一個抽象方法,Java 中的 Lambda 表達式才能對該方法進行推導。

2、函數(shù)式接口格式

在 Java 函數(shù)式接口類中,需要滿足接口類中只能有一個抽象方法??偨Y(jié)如下:

  • 接口有且僅有一個抽象方法;
  • 允許定義靜態(tài)方法;
  • 允許定義默認方法;
  • 允許 java.lang.Object 中的 public 方法;

在創(chuàng)建函數(shù)式接口時,可以在接口類上面加上@FunctionalInterface注解,這時編譯器就可以對接口結(jié)構進行強制檢查是否符合函數(shù)式接口規(guī)則,如果不符合規(guī)則就顯示錯誤。當然,這個注解只是用于檢查,即使不加上該注解,只要符合函數(shù)式接口規(guī)則一樣也是函數(shù)式接口。

下面創(chuàng)建個演示的函數(shù)式接口,如下:

// @FunctionalInterface 注解說明: 
// 使用該注解來定義接口,編譯器會強制檢查接口是否符合函數(shù)式接口規(guī)則(有且僅有一個抽象方法),如果不符合則會報錯。
@FunctionalInterface
public interface MyInterface{
 /**
 * 抽象方法(Jdk 8 以后接口類中方法可以省去 public abstract)
 */
 public abstract [返回值類型] [方法名稱](參數(shù)列表);
 
 /**
 * 其它方法(Jdk 8 以后允許接口類中添加"默認方法"與"靜態(tài)方法" )
 */
 ...(略)
}

按照上面函數(shù)式接口,定義一個示例的函數(shù)式接口類,代碼如下:

@FunctionalInterface
public interface MyCollection {
 
 void push(List list);
 
}

3、函數(shù)式接口和 Lambda 表達式的關系

函數(shù)式接口和 Lambda 表達式的關系可以總結(jié)為:

  • 函數(shù)式接口只包含一個操作方法;
  • Lambda 表達式只能操作一個方法;
  • Java 中的 Lambda 表達式核心就是一個函數(shù)式編程接口的實現(xiàn)。

4、當前 JDK 8 中存在的函數(shù)式接口類

在 JDK 1.8 之前,已經(jīng)存在部分函數(shù)式接口,如下:

  • java.lang.Runnable
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.util.Comparator
  • java.io.FileFilter
  • java.nio.file.PathMatcher
  • java.lang.reflect.InvocationHandler
  • java.beans.PropertyChangeListener
  • java.awt.event.ActionListener
  • javax.swing.event.ChangeListener

在 JDK 8 中新增了函數(shù)接口 java.util.function 包,里面包含了很多用來支持 Java 的函數(shù)式編程的接口類,如下:

類名稱 描述信息
BiConsumer<T,U> 代表了一個接受兩個輸入?yún)?shù)的操作,并且不返回任何結(jié)果。
BiFunction<T,U,R> 代表了一個接受兩個輸入?yún)?shù)的方法,并且返回一個結(jié)果。
BinaryOperator<T> 代表了一個作用于于兩個同類型操作符的操作,并且返回了操作符同類型的結(jié)果。
BiPredicate<T,U> 代表了一個兩個參數(shù)的 boolean 值方法。
BooleanSupplier 代表了 boolean 值結(jié)果的提供方。
Consumer<T> 代表了接受一個輸入?yún)?shù)并且無返回的操作。
DoubleBinaryOperator 代表了作用于兩個 double 值操作符的操作,并且返回了一個 double 值的結(jié)果。
DoubleConsumer 代表一個接受 double 值參數(shù)的操作,并且不返回結(jié)果。
DoubleFunction<R> 代表接受一個 double 值參數(shù)的方法,并且返回結(jié)果。
DoublePredicate 代表一個擁有 double 值參數(shù)的 boolean 值方法。
DoubleSupplier 代表一個 double 值結(jié)構的提供方。
DoubleToIntFunction 接受一個 double 類型輸入,返回一個 int 類型結(jié)果。
DoubleToLongFunction 接受一個 double 類型輸入,返回一個 long 類型結(jié)果。
DoubleUnaryOperator 接受一個參數(shù)同為類型 double,返回值類型也為 double。
Function<T,R> 接受一個輸入?yún)?shù),返回一個結(jié)果。
IntBinaryOperator 接受兩個參數(shù)同為類型 int,返回值類型也為 int。
IntConsumer 接受一個 int 類型的輸入?yún)?shù),無返回值。
IntFunction<R> 接受一個 int 類型輸入?yún)?shù),返回一個結(jié)果。
IntPredicate 接受一個 int 輸入?yún)?shù),返回一個布爾值的結(jié)果。
IntSupplier 無參數(shù),返回一個 int 類型結(jié)果。
IntToDoubleFunction 接受一個 int 類型輸入,返回一個double類型結(jié)果。
IntToLongFunction 接受一個 int 類型輸入,返回一個 long 類型結(jié)果。
IntUnaryOperator 接受一個參數(shù)同為類型 int,返回值類型也為 int。
LongBinaryOperator 接受兩個參數(shù)同為類型 long,返回值類型也為 long。
LongConsumer 接受一個 long 類型的輸入?yún)?shù),無返回值。
LongFunction<R> 接受一個 long 類型輸入?yún)?shù),返回一個結(jié)果。
LongPredicate R接受一個 long 輸入?yún)?shù),返回一個布爾值類型結(jié)果。
LongSupplier 無參數(shù),返回一個結(jié)果 long 類型的值。
LongToDoubleFunction 接受一個 long 類型輸入,返回一個 double 類型結(jié)果。
LongToIntFunction 接受一個 long 類型輸入,返回一個 int 類型結(jié)果。
LongUnaryOperator 接受一個參數(shù)同為類型 long,返回值類型也為 long。
ObjDoubleConsumer<T> 接受一個 object 類型和一個 double 類型的輸入?yún)?shù),無返回值。
ObjIntConsumer<T> 接受一個 object 類型和一個 int 類型的輸入?yún)?shù),無返回值。
ObjLongConsumer<T> 接受一個 object 類型和一個 long 類型的輸入?yún)?shù),無返回值。
Predicate<T> 接受一個輸入?yún)?shù),返回一個布爾值結(jié)果。
Supplier<T> 無參數(shù),返回一個結(jié)果。
ToDoubleBiFunction<T,U> 接受兩個輸入?yún)?shù),返回一個 double 類型結(jié)果
ToDoubleFunction<T> 接受一個輸入?yún)?shù),返回一個 double 類型結(jié)果。
ToIntBiFunction<T,U> 接受兩個輸入?yún)?shù),返回一個 int 類型結(jié)果。
ToIntFunction<T> 接受一個輸入?yún)?shù),返回一個 int 類型結(jié)果。
ToLongBiFunction<T,U> 接受兩個輸入?yún)?shù),返回一個 long 類型結(jié)果。
ToLongFunction<T> 接受一個輸入?yún)?shù),返回一個 long 類型結(jié)果。
UnaryOperator<T> 接受一個參數(shù)為類型 T,返回值類型也為 T。

5、JDK 中常見的函數(shù)式接口類

上面 java.util.function 包提供了眾多的函數(shù)式接口,其中常用的有:

  • java.util.function.Predicate<T>:接收參數(shù)對象 T,返回一個 boolean 類型結(jié)果。
  • java.util.function.Comsumer<T>:接收參數(shù)對象 T,不返回結(jié)果。
  • java.util.function.Function<T,R>:接收參數(shù)對象 T,返回結(jié)果對象 R。
  • java.util.function.Supplier<T>:不接收參數(shù),提供 T 對象的創(chuàng)建工廠。
  • java.util.function.UnaryOperator<T>:接收參數(shù)對象 T,返回結(jié)果對象 T。
  • java.util.function.BinaryOperator<T>:接收兩個 T 對象,返回一個 T 對象結(jié)果。

注:為了使易懂,下面示例中 Lambda 表達式?jīng)]有使用的最簡易寫法,而是使用比較繁瑣的寫法。

(1)、java.util.function.Predicate<T>

接口類作用: 接收參數(shù)對象T,返回一個 boolean 類型結(jié)果。

接口類源碼:

@FunctionalInterface
public interface Predicate<T> {
 /** abstract 方法,接收一個參數(shù), 判斷這個參數(shù)是否匹配某種規(guī)則, 然后返回布爾值結(jié)果 */
 boolean test(T t);
 
 /** default 方法,接收另外一個 Predicate<T> 類型參數(shù)進行"邏輯與"操作,返回一個新的 Predicate */
 default Predicate<T> and(Predicate<? super T> other) {
 Objects.requireNonNull(other);
 return (t) -> test(t) && other.test(t);
 }
 
 /** default 方法,接收另外一個 Predicate<T> 類型參數(shù)進行"邏輯或"操作,返回一個新的 Predicate */
 default Predicate<T> negate() {
 return (t) -> !test(t);
 }
 
 /** default 方法,返回當前 Predicate 取反操作之后的 Predicate */
 default Predicate<T> or(Predicate<? super T> other) {
 Objects.requireNonNull(other);
 return (t) -> test(t) || other.test(t);
 }

 static <T> Predicate<T> isEqual(Object targetRef) {
 return (null == targetRef)
 ? Objects::isNull
 : object -> targetRef.equals(object);
 }
}

使用示例:

public class PredicateExample {

 /** 這里創(chuàng)建一個 Prodicate,設置驗證秘鑰的一個邏輯,然后返回并輸出驗證結(jié)果 */
 public static void main(String[] args) {
 // 創(chuàng)建 Predicate 及 Lambda 表達式與待實現(xiàn)的邏輯
 Predicate<String> validation = (String secret) -> {
 return "123456".equals(secret);
 };
 // 調(diào)用 Predicate 提供的 test 方法并輸出結(jié)果
 System.out.println(validation.test("123"));
 System.out.println(validation.test("123456"));
 } 
 
}

日常開發(fā)中,需要對某個值進行判斷操作,并且返回判斷結(jié)果,這時可以考慮使用 Predicate 接口,以及它的 Lambda 表達式的實現(xiàn),能方便的實現(xiàn)我們業(yè)務上的一些功能。

(2)、java.util.function.Comsumer<T>

接口類作用: 接收參數(shù)對象 T,不返回結(jié)果。

接口類源碼:

@FunctionalInterface
public interface Consumer<T> {

 /** abstract 方法,接收一個參數(shù),執(zhí)行消費邏輯 */
 void accept(T t);
 
 /** default 方法,將兩個 Consumer 連接到一起,再進行消費 */
 default Consumer<T> andThen(Consumer<? super T> after) {
 Objects.requireNonNull(after);
 return (T t) -> { accept(t); after.accept(t); };
 }
 
}

使用示例:

/** 這里創(chuàng)建一個 Consumer,模擬發(fā)送消息并打印內(nèi)容 */
public class ConsumerExample {

 public static void main(String[] args) {
 // 創(chuàng)建 Consumer 及 Lambda 表達式與待實現(xiàn)的邏輯
 Consumer<String> consumer = (String message) -> {
 System.out.println("發(fā)送消息內(nèi)容:" + message);
 };
 // 調(diào)用 Consumer 提供的 accept 方法
 consumer.accept("測試消息");
 } 
 
}

日常開發(fā)中,需要對某個類型進行公共處理,并且不需要任何返回值,這時可以考慮使用 Consumer 接口及它的 Lambda 表達式的實現(xiàn),能方便的實現(xiàn)我們業(yè)務上的一些功能。

(3)、java.util.function.Function<T,R>

接口類作用: 接收參數(shù)對象 T,返回結(jié)果對象 R。

接口類源碼:

@FunctionalInterface
public interface Function<T, R> {
 /**abstract 方法,接收一個參數(shù)進行處理,然后返回處理結(jié)果 R */
 R apply(T t);

 /** default 方法,先執(zhí)行參數(shù)(Function)的,再執(zhí)行調(diào)用者(Function) */
 default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
 Objects.requireNonNull(before);
 return (V v) -> apply(before.apply(v));
 }
 
 /** default 方法,先執(zhí)行調(diào)用者,再執(zhí)行參數(shù),和compose相反 */
 default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
 Objects.requireNonNull(after);
 return (T t) -> after.apply(apply(t));
 }
 
 /** 返回當前正在執(zhí)行的方法 */
 static <T> Function<T, T> identity() {
 return t -> t;
 }
}

使用示例:

/** 這里創(chuàng)建一個 Function,對傳入的參數(shù)進行驗證,如果包含 a 字符就返回1,否則返回0 */
public class FunctionExample {

 public static void main(String[] args) {
 // 創(chuàng)建 Function 及 Lambda 表達式與待實現(xiàn)的邏輯
 Function<String, Integer> function = (String str) -> {
 return str.contains("a") ? 1 : 0;
 };
 // 調(diào)用 Function 提供的 apply 方法
 System.out.println(function.apply("abcd"));
 System.out.println(function.apply("efgh"));
 }
 
}

日常開發(fā)中,需要對某個類型數(shù)據(jù)進行操作,經(jīng)過一系列邏輯后轉(zhuǎn)換為一個新的類型進行返回,這時可以考慮使用 Function 接口及它的 Lambda 表達式的實現(xiàn),能方便的實現(xiàn)我們業(yè)務上的一些功能。

(4)、java.util.function.Supplier<T>

接口類作用: 不接收參數(shù),提供 T 對象的創(chuàng)建工廠。

接口類源碼:

@FunctionalInterface
public interface Supplier<T> {
 /** abstract 方法,設置業(yè)務邏輯,獲取邏輯中創(chuàng)建的對象 */
 T get();
}

使用示例:

/** 這里創(chuàng)建一個 Supplier,用于生成隨機ID,通過 get 方法獲取生成的隨機ID值 */
public class SupplierExample {

 public static void main(String[] args) {
 // 創(chuàng)建 Supplier 及 Lambda 表達式與待實現(xiàn)的邏輯
 Supplier<String> supplier = () -> {
 return UUID.randomUUID().toString();
 };
 // 調(diào)用 Supplier 提供的 get 方法
 System.out.println(supplier.get());
 }

}

日常開發(fā)中,需要創(chuàng)建一個統(tǒng)一的工廠用于生成特定的產(chǎn)物完成特定的目標,這時就可以考慮使用 Supplier 接口及它的 Lambda 表達式的實現(xiàn),能方便的實現(xiàn)我們業(yè)務上的一些功能。

(5)、java.util.function.UnaryOperator<T>

接口類作用: 接收參數(shù)對象 T,返回結(jié)果對象 T。

接口類源碼:

// 可以看到 UnaryOperator 繼承了 Function 接口
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
 /** static 方法,接收一個參數(shù),然后對其處理后再返回 */
 static <T> UnaryOperator<T> identity() {
 return t -> t;
 }
}

使用示例:

/** 這里創(chuàng)建一個 UnaryOperator,接收一個字符串進行加工處理后返回新字符串 */
public class UnaryOperatorExample {
 
 public static void main(String[] args) {
 // 創(chuàng)建 UnaryOperator 及 Lambda 表達式與待實現(xiàn)的邏輯
 UnaryOperator<String> unaryOperator = (String str) -> {
 return "[" + str + "]";
 };
 // 調(diào)用 UnaryOperator 繼承的 Function 提供的 apply 方法
 System.out.println(unaryOperator.apply("hello"));
 }

}

日常開發(fā)中,我們經(jīng)常要對一個已有的對象進行操作修改,然后返回修改后的對象,這時就可以考慮使用 UnaryOperator 接口及它的 Lambda 表達式的實現(xiàn),能方便的實現(xiàn)我們業(yè)務上的一些功能。

(6)、java.util.function.BinaryOperator<T>

接口類作用: 接收兩個 T 對象,返回一個 T 對象結(jié)果。

接口類源碼:

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
 /** abstract 方法,通過比較器Comparator來比較兩個元素中較小的一個作為返回值返回 */
 public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
 Objects.requireNonNull(comparator);
 return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
 }
 
 /** 通過比較器Comparator來比較兩個元素中較大的一個作為返回值返回 */
 public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
 Objects.requireNonNull(comparator);
 return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
 }
}

使用示例:

/** 這里創(chuàng)建一個 BinaryOperator,比較傳入的兩個參數(shù)哪個值最大,返回最大值 */
public class BinaryOperatorExample {

 public static void main(String[] args) {
 // 創(chuàng)建 BinaryOperator 及 Lambda 表達式與待實現(xiàn)的邏輯
 BinaryOperator<Integer> binaryOperator = (Integer t1, Integer t2) -> {
 return t1 > t2 ? t1 : t2;
 };
 // 調(diào)用 BinaryOperator 繼承的 BiFunction 提供的 apply 方法
 System.out.println(binaryOperator.apply(1, 2));
 }

}

在使用這幾種基本函數(shù)接口時傳入?yún)?shù) T 不能是基本類型,如 BinaryOperator 中 T 不能設置為 int,只能使用 Integer 包裝類,這也限制了 Lambda 表達式中設置參數(shù)時候,使用包裝類替換基本類型。

日常開發(fā)中,我們有時候會對兩個對象進行操作,執(zhí)行一些操作邏輯后返回結(jié)果,這時就可以考慮使用 BinaryOperator 接口及它的 Lambda 表達式的實現(xiàn),能方便的實現(xiàn)我們業(yè)務上的一些功能。

三、Lambda 表達式基本語法

1、Lambda 表達式的組成

Lambda 表達式的組成可以拆分為:

  • 聲明: 與 Lambda 表達式綁定的接口類型。
  • 參數(shù): 參數(shù)包含在一對 () 中,和綁定的接口中的抽象方法中的參數(shù)個數(shù)及順序一致。
  • 操作符: ->
  • 執(zhí)行代碼塊: 執(zhí)行代碼塊包含在一對 {} 中,出現(xiàn)在操作符的右側(cè)。           

[接口聲明] = (參數(shù)) -> {執(zhí)行代碼塊}

2、Lambda 表達式的格式

Lambda 表達式可以分為下面幾種格式:

  • 無參數(shù),無返回值;
  • 有一個參數(shù),無返回值;
  • 左側(cè)只有一個參數(shù),小括號可以省略不寫;
  • 有兩個以上參數(shù),有返回值,并且Lambda 體中有多條語句;
  • 若右側(cè)Lambda體中,只有一條語句,return 和大括號都可以省略不寫;
  • Lambda 表達式的參數(shù)列表的數(shù)據(jù)類型可以省略不寫,jvm編譯器會進行上下文推斷出,數(shù)據(jù)類型“類型推斷”;

(1)、無參數(shù),無返回值

() -> System.out.println("測試");

(2)、有一個參數(shù),無返回值

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

(3)、左側(cè)只有一個參數(shù),小括號可以省略不寫

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

(4)、有兩個以上參數(shù),有返回值,并且Lambda 體中有多條語句

Comparator<Integer> comparator = (x, y) -> {
 System.out.println("測試");
 return Integer.compare(x,y);
};

(5)、若右側(cè)Lambda體中,只有一條語句,return 和大括號都可以省略不寫

Comparator<Integer> Comparator = (x, y) -> Integer.compare(x, y);

(6)、Lambda 表達式的參數(shù)列表的數(shù)據(jù)類型可以省略不寫,JVM 在運行時,會自動根據(jù)綁定的抽象方法中的參數(shù),進行數(shù)據(jù)類型推導

(Integer x, Integer y) -> Integer.compare();

四、Lambda 表達式中變量作用域

Java 中的變量捕獲與變量隱藏:

  • 變量捕獲: 局部類和匿名內(nèi)部類可以訪問被 final 修飾的封閉塊內(nèi)的局部變量。
  • 變量隱藏: 在一個類中,子類中的成員變量如果和父類中的成員變量同名,那么即使他們類型不一樣,只要名字一樣,父類中的成員變量都會被隱藏。

在局部類和匿名內(nèi)部類都存在 變量捕獲 與 變量隱藏,而在 Lambda 表達式中則只支持 變量捕獲。

下面是對這作用域得演示示例:

(1)、匿名內(nèi)部類:

public class VariableExample {
 /** 成員變量 */
 String str1 = "成員變量";

 public void innerClass() {
 // 方法內(nèi)部變量
 String str2 = "方法內(nèi)部變量";
 // 使用匿名內(nèi)部類創(chuàng)建線程
 new Thread(new Runnable() {
  // 匿名內(nèi)部類內(nèi)部變量
  String str3 = "匿名內(nèi)部類內(nèi)部變量";
  @Override
  public void run() {
  /* 訪問變量 */
  System.out.println("匿名內(nèi)部類輸出:" + str1);
  System.out.println("匿名內(nèi)部類輸出:" + str2);
  System.out.println("匿名內(nèi)部類輸出:" + str3);
  /* 修改變量 */
  str1 = "修改訪問成員變量";
  // str2 = "修改訪問方法內(nèi)部變量"; // 不能進行修改,默認推導變量的修飾符 final
  str3 = "修改訪問匿名內(nèi)部類內(nèi)部變量";
  /* 在匿名內(nèi)部類中定義和類外部變量一樣名稱的變量 */
  String str1 = "重新命名成員變量";
  String str2 = "重新命名方法內(nèi)部變量";
  }
 }).start();
 }
 
 /** Main 方法 */
 public static void main(String[] args) {
 VariableExample variableExample = new VariableExample();
 // 匿名內(nèi)部類
 variableExample.innerClass();
 }

}

(2)、Lambda 表達式:

public class VariableExample {
 /** 成員變量 */
 String str1 = "成員變量";
 
 public void lambdaExpression() {
 // 方法內(nèi)部變量
 String str2 = "方法內(nèi)部變量";
 new Thread(()->{
  // Lambda 內(nèi)部變量
  String str3 = "Lambda 內(nèi)部變量";
  /* 訪問變量 */
  // 訪問成員變量
  System.out.println("Lambda 表達式輸出:" + str1);
  // 訪問方法內(nèi)部變量
  System.out.println("Lambda 表達式輸出:" + str2);
  // 訪問匿名內(nèi)部類內(nèi)部變量
  System.out.println("Lambda 表達式輸出:" + str3);
  /* 修改變量 */
  str1 = "修改訪問成員變量";
  // str2 = "修改訪問方法內(nèi)部變量"; // 不能進行修改,默認推導變量的修飾符 final
  str3 = "修改訪問匿名內(nèi)部類內(nèi)部變量";
  /* 在 Lambda 中定義和類外部變量一樣名稱的變量 */
  String str1 = "重新命名成員變量";
  // String str2 = "重新命名方法內(nèi)部變量"; // 不能命名,lambda 不支持變量隱藏
 }).start();
 }

 /** Main 方法 */
 public static void main(String[] args) {
 VariableExample variableExample = new VariableExample();
 // 匿名內(nèi)部類
 variableExample.innerClass();
 // Lambda 表達式
 variableExample.lambdaExpression();
 }

}

五、Lambda 表達式方法重載問題

       當使用 Lambda 表達式,調(diào)用一個類中的重載方法,且方法中的參數(shù)為都為函數(shù)接口,函數(shù)接口中定義的方法接收的參數(shù)類型相同,這時候 Lambda 是無法推斷出要調(diào)用哪個方法。

函數(shù)接口A:

@FunctionalInterface
interface MyInterfaceA {
 void push(String param);
}

函數(shù)接口B:

@FunctionalInterface
interface MyInterfaceB {
 void pull(String param);
}

示例,實現(xiàn)方法重載與測試的 Main 方法:

public class LambdaOverloadExample {
 
 // 重載方法A
 public static void method(MyInterfaceA myInterfaceA) {
 myInterfaceA.push("hello 1");
 }
 // 重載方法B
 public static void method(MyInterfaceB myInterfaceB) {
 myInterfaceB.pull("Hello 2");
 }

 /** Main 方法*/
 public static void main(String[] args) {
 // 使用匿名內(nèi)部類
 method(new MyInterfaceA() {
  @Override
  public void push(String param) {
  System.out.println(param);
  }
 });
 method(new MyInterfaceB() {
  @Override
  public void pull(String param) {
  System.out.println(param);
  }
 });
 // 使用 Lambda 表達式
 //method(param -> System.out.println(param)); // 編譯器提示錯誤,表示無法推斷使用哪個參數(shù)
 }

}

上面注掉的那部分代碼,在編輯器中直接提示錯誤,很顯然 Lambda 表達式無法直接推斷出使用哪個類中的重載方法。其實,只要明確告訴 Lambda 表達式使用哪個參數(shù),就可以很簡單的解決問題,比如以上面的例子,在 Lambda 表達式使用 method 方法時,將參數(shù)類型轉(zhuǎn)換為對應的要使用的類型就可以解決這個問題,代碼如下:

// 轉(zhuǎn)換參數(shù)為 MyInterfaceA
method((MyInterfaceA)param -> System.out.println(param));
// 轉(zhuǎn)換參數(shù)為 MyInterfaceB
method((MyInterfaceB)param -> System.out.println(param));

按上面進行修改后就可以正常使用 Lambda 表達式了,如果不習慣也可以使用匿名內(nèi)部類進行方法調(diào)用,內(nèi)名內(nèi)部類是沒有相關問題的。

六、Lambda 表達式方法引用

       方法引用本質(zhì)上就是對方法調(diào)用的簡化,方法引用和函數(shù)式接口綁定,在使用過程中會創(chuàng)建函數(shù)式接口的實例,是結(jié)合 Lambda 表達式的一種特性。在應用過程中,方法引用常分為:

  • 靜態(tài)方法引用
  • 實例方法引用
  • 構造方法引用
  • 特定類型的任意對象實例方法引用

注意:在使用 Lmabda 方法引用時雖然能夠簡化代碼,但是在實際開發(fā)中不可因需要簡化代碼而過度使用方法引用,因為他會在很大程度上降低代碼可讀性。

1、創(chuàng)建示例的實體類

為了下面示例方便,我們首先創(chuàng)建一個 Person 實體類,如下:

public class Person {
 /** 姓名 */
 private String name;
 /** 歲數(shù) */
 private int age;

 public Person() {
 }
 public Person(String name, int age) {
 this.name = name;
 this.age = age;
 }

 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public int getAge() {
 return age;
 }
 public void setAge(int age) {
 this.age = age;
 }
 
 @Override
 public String toString() {
 return "Person{name=" + name + ",age=" + age + "}";
 }
}

2、靜態(tài)方法引用示例

靜態(tài)方法的引用的使用: 靜態(tài)方法所在類.方法名稱() –> 靜態(tài)方法所在類 :: 方法名稱

創(chuàng)建一個使用靜態(tài)方法引用的示例類:

public class StaticMethodExample {

 /** 測試的靜態(tài)方法 */
 public static int compareAge(Person p1, Person p2) {
 return p1.getAge() - p2.getAge();
 }

 public static void main(String[] args) {
 // 創(chuàng)建 Person 集合
 List<Person> personList = new ArrayList<>();
 personList.add(new Person("Wangqin",26));
 personList.add(new Person("Liming",22));
 personList.add(new Person("Alisi",18));
 personList.add(new Person("Jerry",31));
 
 // 按歲數(shù)進行排序,使用靜態(tài)方法引用
 personList.sort(StaticMethodExample::compareAge);
 }

}

3、實例方法引用示例

實例方法的引用的使用:創(chuàng)建類型對應一個對象 –> 對應應用 :: 實例方法名稱

創(chuàng)建一個封裝實例方法的類:

public class PersonUtil{
 /** 測試的實例方法 */
 public int compareAge(Person p1, Person p2) {
 return p1.getAge() - p2.getAge();
 }
}

創(chuàng)建一個使用實例方法引用的示例類:

public class InstanceMethodExample {

 public static void main(String[] args) {
 // 創(chuàng)建 Person 集合
 List<Person> personList = new ArrayList<>();
 personList.add(new Person("Wangqin",26));
 personList.add(new Person("Liming",22));
 personList.add(new Person("Alisi",18));
 personList.add(new Person("Jerry",31));
 
 // 按歲數(shù)進行排序,
 PersonUtil personUtil = new PersonUtil();
 // 引用實例方法
 personList.sort(personUtil::compareAge);
 }

}

4、構造方法引用示例

構造方法的引用的使用:綁定函數(shù)式接口

創(chuàng)建一個函數(shù)式接口,且設置接收參數(shù)和 Person 的構造方法相同,返回 Person 對象的方法定義:

@FunctionalInterface
public interface PersonConstructor{
 Person initInstance(String name, int age);
}

創(chuàng)建一個使用構造方法引用的示例類

public class LambdaConstructorMethodExample {

 public static void main(String[] args) {
 // 創(chuàng)建 Person 集合
 List<Person> personList = new ArrayList<>();
 personList.add(new Person("Wangqin", 26));
 personList.add(new Person("Liming", 22));
 personList.add(new Person("Alisi", 18));
 personList.add(new Person("Jerry", 31));

 // 構造方法引用
 PersonConstructor personConstructor = Person::new;
 Person person = personConstructor.initInstance("linda", 18);
 System.out.println(person);
 }
 
}

5、特定類型的任意對象實例方法引用示例

特定類型的任意對象實例方法引用示例:特定類型 :: 特定類型的方法

以下是對特定類型的任意對象的實例方法的引用的示例:

public class LambdaExample {

 public static void main(String[] args) {
 // 創(chuàng)建字符串集合
 List<String> strList = new ArrayList<>();
 strList.add("Jerry");
 strList.add("Mini");
 strList.add("Kary");
 strList.add("walls");
 
 // 使用集合的sort方法,按照String的compareToIgnoreCase進行排序(比較字符串hash值)
 strList.sort(String::compareToIgnoreCase);
 System.out.println(strList);
 }

}

這里根據(jù)定義的集合 strList 去推導目標類型參數(shù)值,如果不符合后面?zhèn)魅氲姆椒ㄒ盟鶎念愋?,將報錯。該方法參考等效 Lambda 表達式 String::compareToIgnoreCase 的參數(shù)列表 (String a, String b),其中 a 和 b 是用于更好地描述這個例子中的任意名稱。方法引用將調(diào)用該方法 a.compareToIgnoreCase(b)。

到此這篇關于Java8如何使用Lambda表達式簡化代碼的文章就介紹到這了,更多相關Java8用Lambda表達式簡化代碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論