Java8如何使用Lambda表達式簡化代碼詳解
系統(tǒng)環(huán)境:
- Java JDK 版本:1.8
參考地址:
一、Lambda 表達式簡介
1、什么是 Lambda 表達式
Lambda 表達式是在 JDK 8 中引入的一個新特性,可用于取代大部分的匿名內部類。使用 Lambda 表達式可以完成用少量的代碼實現(xiàn)復雜的功能,極大的簡化代碼代碼量和代碼結構。同時,JDK 中也增加了大量的內置函數(shù)式接口供我們使用,使得在使用 Lambda 表達式時更加簡單、高效。
2、為什么需要 Lambda 表達式
談起為什么需要 Lambda 表達式,那得從函數(shù)式編程開始說起。函數(shù)式編程可以簡單說是一種編程規(guī)范,也就是如何編寫程序的方法論。它屬于結構化編程的一種,主要思想是把運算過程盡量寫成一系列嵌套的函數(shù)調用。函數(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ù)式接口被隱式轉換為 Lambda 表達式,只有保證接口類中有且只有一個抽象方法,Java 中的 Lambda 表達式才能對該方法進行推導。
2、函數(shù)式接口格式
在 Java 函數(shù)式接口類中,需要滿足接口類中只能有一個抽象方法。總結如下:
- 接口有且僅有一個抽象方法;
- 允許定義靜態(tài)方法;
- 允許定義默認方法;
- 允許 java.lang.Object 中的 public 方法;
在創(chuàng)建函數(shù)式接口時,可以在接口類上面加上@FunctionalInterface注解,這時編譯器就可以對接口結構進行強制檢查是否符合函數(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 表達式的關系可以總結為:
- 函數(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ù)的操作,并且不返回任何結果。 |
| BiFunction<T,U,R> | 代表了一個接受兩個輸入?yún)?shù)的方法,并且返回一個結果。 |
| BinaryOperator<T> | 代表了一個作用于于兩個同類型操作符的操作,并且返回了操作符同類型的結果。 |
| BiPredicate<T,U> | 代表了一個兩個參數(shù)的 boolean 值方法。 |
| BooleanSupplier | 代表了 boolean 值結果的提供方。 |
| Consumer<T> | 代表了接受一個輸入?yún)?shù)并且無返回的操作。 |
| DoubleBinaryOperator | 代表了作用于兩個 double 值操作符的操作,并且返回了一個 double 值的結果。 |
| DoubleConsumer | 代表一個接受 double 值參數(shù)的操作,并且不返回結果。 |
| DoubleFunction<R> | 代表接受一個 double 值參數(shù)的方法,并且返回結果。 |
| DoublePredicate | 代表一個擁有 double 值參數(shù)的 boolean 值方法。 |
| DoubleSupplier | 代表一個 double 值結構的提供方。 |
| DoubleToIntFunction | 接受一個 double 類型輸入,返回一個 int 類型結果。 |
| DoubleToLongFunction | 接受一個 double 類型輸入,返回一個 long 類型結果。 |
| DoubleUnaryOperator | 接受一個參數(shù)同為類型 double,返回值類型也為 double。 |
| Function<T,R> | 接受一個輸入?yún)?shù),返回一個結果。 |
| IntBinaryOperator | 接受兩個參數(shù)同為類型 int,返回值類型也為 int。 |
| IntConsumer | 接受一個 int 類型的輸入?yún)?shù),無返回值。 |
| IntFunction<R> | 接受一個 int 類型輸入?yún)?shù),返回一個結果。 |
| IntPredicate | 接受一個 int 輸入?yún)?shù),返回一個布爾值的結果。 |
| IntSupplier | 無參數(shù),返回一個 int 類型結果。 |
| IntToDoubleFunction | 接受一個 int 類型輸入,返回一個double類型結果。 |
| IntToLongFunction | 接受一個 int 類型輸入,返回一個 long 類型結果。 |
| IntUnaryOperator | 接受一個參數(shù)同為類型 int,返回值類型也為 int。 |
| LongBinaryOperator | 接受兩個參數(shù)同為類型 long,返回值類型也為 long。 |
| LongConsumer | 接受一個 long 類型的輸入?yún)?shù),無返回值。 |
| LongFunction<R> | 接受一個 long 類型輸入?yún)?shù),返回一個結果。 |
| LongPredicate | R接受一個 long 輸入?yún)?shù),返回一個布爾值類型結果。 |
| LongSupplier | 無參數(shù),返回一個結果 long 類型的值。 |
| LongToDoubleFunction | 接受一個 long 類型輸入,返回一個 double 類型結果。 |
| LongToIntFunction | 接受一個 long 類型輸入,返回一個 int 類型結果。 |
| 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ù),返回一個布爾值結果。 |
| Supplier<T> | 無參數(shù),返回一個結果。 |
| ToDoubleBiFunction<T,U> | 接受兩個輸入?yún)?shù),返回一個 double 類型結果 |
| ToDoubleFunction<T> | 接受一個輸入?yún)?shù),返回一個 double 類型結果。 |
| ToIntBiFunction<T,U> | 接受兩個輸入?yún)?shù),返回一個 int 類型結果。 |
| ToIntFunction<T> | 接受一個輸入?yún)?shù),返回一個 int 類型結果。 |
| ToLongBiFunction<T,U> | 接受兩個輸入?yún)?shù),返回一個 long 類型結果。 |
| ToLongFunction<T> | 接受一個輸入?yún)?shù),返回一個 long 類型結果。 |
| UnaryOperator<T> | 接受一個參數(shù)為類型 T,返回值類型也為 T。 |
5、JDK 中常見的函數(shù)式接口類
上面 java.util.function 包提供了眾多的函數(shù)式接口,其中常用的有:
- java.util.function.Predicate<T>:接收參數(shù)對象 T,返回一個 boolean 類型結果。
- java.util.function.Comsumer<T>:接收參數(shù)對象 T,不返回結果。
- java.util.function.Function<T,R>:接收參數(shù)對象 T,返回結果對象 R。
- java.util.function.Supplier<T>:不接收參數(shù),提供 T 對象的創(chuàng)建工廠。
- java.util.function.UnaryOperator<T>:接收參數(shù)對象 T,返回結果對象 T。
- java.util.function.BinaryOperator<T>:接收兩個 T 對象,返回一個 T 對象結果。
注:為了使易懂,下面示例中 Lambda 表達式?jīng)]有使用的最簡易寫法,而是使用比較繁瑣的寫法。
(1)、java.util.function.Predicate<T>
接口類作用: 接收參數(shù)對象T,返回一個 boolean 類型結果。
接口類源碼:
@FunctionalInterface
public interface Predicate<T> {
/** abstract 方法,接收一個參數(shù), 判斷這個參數(shù)是否匹配某種規(guī)則, 然后返回布爾值結果 */
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,設置驗證秘鑰的一個邏輯,然后返回并輸出驗證結果 */
public static void main(String[] args) {
// 創(chuàng)建 Predicate 及 Lambda 表達式與待實現(xiàn)的邏輯
Predicate<String> validation = (String secret) -> {
return "123456".equals(secret);
};
// 調用 Predicate 提供的 test 方法并輸出結果
System.out.println(validation.test("123"));
System.out.println(validation.test("123456"));
}
}
日常開發(fā)中,需要對某個值進行判斷操作,并且返回判斷結果,這時可以考慮使用 Predicate 接口,以及它的 Lambda 表達式的實現(xiàn),能方便的實現(xiàn)我們業(yè)務上的一些功能。
(2)、java.util.function.Comsumer<T>
接口類作用: 接收參數(shù)對象 T,不返回結果。
接口類源碼:
@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ā)送消息并打印內容 */
public class ConsumerExample {
public static void main(String[] args) {
// 創(chuàng)建 Consumer 及 Lambda 表達式與待實現(xiàn)的邏輯
Consumer<String> consumer = (String message) -> {
System.out.println("發(fā)送消息內容:" + message);
};
// 調用 Consumer 提供的 accept 方法
consumer.accept("測試消息");
}
}
日常開發(fā)中,需要對某個類型進行公共處理,并且不需要任何返回值,這時可以考慮使用 Consumer 接口及它的 Lambda 表達式的實現(xiàn),能方便的實現(xiàn)我們業(yè)務上的一些功能。
(3)、java.util.function.Function<T,R>
接口類作用: 接收參數(shù)對象 T,返回結果對象 R。
接口類源碼:
@FunctionalInterface
public interface Function<T, R> {
/**abstract 方法,接收一個參數(shù)進行處理,然后返回處理結果 R */
R apply(T t);
/** default 方法,先執(zhí)行參數(shù)(Function)的,再執(zhí)行調用者(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í)行調用者,再執(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;
};
// 調用 Function 提供的 apply 方法
System.out.println(function.apply("abcd"));
System.out.println(function.apply("efgh"));
}
}
日常開發(fā)中,需要對某個類型數(shù)據(jù)進行操作,經(jīng)過一系列邏輯后轉換為一個新的類型進行返回,這時可以考慮使用 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();
};
// 調用 Supplier 提供的 get 方法
System.out.println(supplier.get());
}
}
日常開發(fā)中,需要創(chuàng)建一個統(tǒng)一的工廠用于生成特定的產物完成特定的目標,這時就可以考慮使用 Supplier 接口及它的 Lambda 表達式的實現(xiàn),能方便的實現(xiàn)我們業(yè)務上的一些功能。
(5)、java.util.function.UnaryOperator<T>
接口類作用: 接收參數(shù)對象 T,返回結果對象 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 + "]";
};
// 調用 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 對象結果。
接口類源碼:
@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;
};
// 調用 BinaryOperator 繼承的 BiFunction 提供的 apply 方法
System.out.println(binaryOperator.apply(1, 2));
}
}
在使用這幾種基本函數(shù)接口時傳入?yún)?shù) T 不能是基本類型,如 BinaryOperator 中 T 不能設置為 int,只能使用 Integer 包裝類,這也限制了 Lambda 表達式中設置參數(shù)時候,使用包裝類替換基本類型。
日常開發(fā)中,我們有時候會對兩個對象進行操作,執(zhí)行一些操作邏輯后返回結果,這時就可以考慮使用 BinaryOperator 接口及它的 Lambda 表達式的實現(xiàn),能方便的實現(xiàn)我們業(yè)務上的一些功能。
三、Lambda 表達式基本語法
1、Lambda 表達式的組成
Lambda 表達式的組成可以拆分為:
- 聲明: 與 Lambda 表達式綁定的接口類型。
- 參數(shù): 參數(shù)包含在一對 () 中,和綁定的接口中的抽象方法中的參數(shù)個數(shù)及順序一致。
- 操作符: ->
- 執(zhí)行代碼塊: 執(zhí)行代碼塊包含在一對 {} 中,出現(xiàn)在操作符的右側。
[接口聲明] = (參數(shù)) -> {執(zhí)行代碼塊}
2、Lambda 表達式的格式
Lambda 表達式可以分為下面幾種格式:
- 無參數(shù),無返回值;
- 有一個參數(shù),無返回值;
- 左側只有一個參數(shù),小括號可以省略不寫;
- 有兩個以上參數(shù),有返回值,并且Lambda 體中有多條語句;
- 若右側Lambda體中,只有一條語句,return 和大括號都可以省略不寫;
- Lambda 表達式的參數(shù)列表的數(shù)據(jù)類型可以省略不寫,jvm編譯器會進行上下文推斷出,數(shù)據(jù)類型“類型推斷”;
(1)、無參數(shù),無返回值
() -> System.out.println("測試");
(2)、有一個參數(shù),無返回值
(x) -> System.out.println(x);
(3)、左側只有一個參數(shù),小括號可以省略不寫
x -> System.out.println(x);
(4)、有兩個以上參數(shù),有返回值,并且Lambda 體中有多條語句
Comparator<Integer> comparator = (x, y) -> {
System.out.println("測試");
return Integer.compare(x,y);
};
(5)、若右側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 中的變量捕獲與變量隱藏:
- 變量捕獲: 局部類和匿名內部類可以訪問被 final 修飾的封閉塊內的局部變量。
- 變量隱藏: 在一個類中,子類中的成員變量如果和父類中的成員變量同名,那么即使他們類型不一樣,只要名字一樣,父類中的成員變量都會被隱藏。
在局部類和匿名內部類都存在 變量捕獲 與 變量隱藏,而在 Lambda 表達式中則只支持 變量捕獲。
下面是對這作用域得演示示例:
(1)、匿名內部類:
public class VariableExample {
/** 成員變量 */
String str1 = "成員變量";
public void innerClass() {
// 方法內部變量
String str2 = "方法內部變量";
// 使用匿名內部類創(chuàng)建線程
new Thread(new Runnable() {
// 匿名內部類內部變量
String str3 = "匿名內部類內部變量";
@Override
public void run() {
/* 訪問變量 */
System.out.println("匿名內部類輸出:" + str1);
System.out.println("匿名內部類輸出:" + str2);
System.out.println("匿名內部類輸出:" + str3);
/* 修改變量 */
str1 = "修改訪問成員變量";
// str2 = "修改訪問方法內部變量"; // 不能進行修改,默認推導變量的修飾符 final
str3 = "修改訪問匿名內部類內部變量";
/* 在匿名內部類中定義和類外部變量一樣名稱的變量 */
String str1 = "重新命名成員變量";
String str2 = "重新命名方法內部變量";
}
}).start();
}
/** Main 方法 */
public static void main(String[] args) {
VariableExample variableExample = new VariableExample();
// 匿名內部類
variableExample.innerClass();
}
}
(2)、Lambda 表達式:
public class VariableExample {
/** 成員變量 */
String str1 = "成員變量";
public void lambdaExpression() {
// 方法內部變量
String str2 = "方法內部變量";
new Thread(()->{
// Lambda 內部變量
String str3 = "Lambda 內部變量";
/* 訪問變量 */
// 訪問成員變量
System.out.println("Lambda 表達式輸出:" + str1);
// 訪問方法內部變量
System.out.println("Lambda 表達式輸出:" + str2);
// 訪問匿名內部類內部變量
System.out.println("Lambda 表達式輸出:" + str3);
/* 修改變量 */
str1 = "修改訪問成員變量";
// str2 = "修改訪問方法內部變量"; // 不能進行修改,默認推導變量的修飾符 final
str3 = "修改訪問匿名內部類內部變量";
/* 在 Lambda 中定義和類外部變量一樣名稱的變量 */
String str1 = "重新命名成員變量";
// String str2 = "重新命名方法內部變量"; // 不能命名,lambda 不支持變量隱藏
}).start();
}
/** Main 方法 */
public static void main(String[] args) {
VariableExample variableExample = new VariableExample();
// 匿名內部類
variableExample.innerClass();
// Lambda 表達式
variableExample.lambdaExpression();
}
}
五、Lambda 表達式方法重載問題
當使用 Lambda 表達式,調用一個類中的重載方法,且方法中的參數(shù)為都為函數(shù)接口,函數(shù)接口中定義的方法接收的參數(shù)類型相同,這時候 Lambda 是無法推斷出要調用哪個方法。
函數(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) {
// 使用匿名內部類
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ù)類型轉換為對應的要使用的類型就可以解決這個問題,代碼如下:
// 轉換參數(shù)為 MyInterfaceA method((MyInterfaceA)param -> System.out.println(param)); // 轉換參數(shù)為 MyInterfaceB method((MyInterfaceB)param -> System.out.println(param));
按上面進行修改后就可以正常使用 Lambda 表達式了,如果不習慣也可以使用匿名內部類進行方法調用,內名內部類是沒有相關問題的。
六、Lambda 表達式方法引用
方法引用本質上就是對方法調用的簡化,方法引用和函數(shù)式接口綁定,在使用過程中會創(chuàng)建函數(shù)式接口的實例,是結合 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 是用于更好地描述這個例子中的任意名稱。方法引用將調用該方法 a.compareToIgnoreCase(b)。
到此這篇關于Java8如何使用Lambda表達式簡化代碼的文章就介紹到這了,更多相關Java8用Lambda表達式簡化代碼內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringCloud開啟session共享并存儲到Redis的實現(xiàn)
這篇文章主要介紹了SpringCloud開啟session共享并存儲到Redis的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02

