首頁  >  文章  >  Java  >  Java的Lambda表達式使用實例分析

Java的Lambda表達式使用實例分析

WBOY
WBOY轉載
2023-04-18 16:01:03717瀏覽

Lambada 簡介

lambda 表達式 是Java 8新加入的新特性,它在Java中是引入了函數式程式設計這個概念。那什麼是函數式程式設計呢?

函數式程式設計:函數式程式設計是數學導向的抽象,將計算描述為一種表達式求值。

我們平常所說的物件導向程式設計屬於命令式程式設計,函數式程式設計和命令式程式設計的差別是:

  • 函數式程式設計關心資料的映射,命令式程式關係解決問題的步驟。

  • 函數式程式關係型別(代數結構)之間的關係,命令式程式設計關係解決問題的步驟。

函數式程式設計的本質:

#函數式程式設計中的函數指的不是電腦中的函數,而是數學中的函數,即自變數的映射。即:一個函數的值只取決於函數參數的值,不依賴其他狀態。

嚴格意義上的函數式程式設計意味著不使用可變的變量,賦值,循環和其他命令式控制結構進行程式設計。

函數式程式設計的好處:

函數式程式設計的好處是主要是不可變性帶來的。沒有可變的狀態,函數就是引用透明(Referential transparency)的和沒有副作用的(No Side Effect)。

上面這些都是一些基本的概念,但是我們平常可能接觸這些方面的東西比較少,所以一開始感覺函數式程式設計是很難得東西。

簡單的範例

Talk is cheap, show me the code!

先來一個最簡單的例子,可能也是介紹的最多的例子了。哈哈!

為按鈕新增監視器。

使用匿名內部類別的方式,進行新增。

submit.addActionListener(new ActionListener() {
	@Override
	public void actionPerformed(ActionEvent e) {
		JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);
	}
});

這種方式的缺點:使用了很多的模板程式碼,真正必要的程式碼,只是方法體內的程式碼。所以,Java 8 引入的 Lambda 表達式可以簡化這種程式碼(當然了,也是有限制的,不是所有的匿名內部類別都可以,這個後面會提到。)。

使用 Lambda 表達式 簡化程式碼

submit.addActionListener((e)->{
		JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);
});

#Lambda 表達式是一個匿名方法,將行為像資料一樣傳遞。

說明

可以看出來,使用Lambda 表達式簡化後的程式碼表達變得更加清晰了,並且不用再寫入繁瑣的模板程式碼了。

當進一步簡化

參數括號和程式碼體的花括號也可以省略(只有一個參數時,可以省略圓括號,只有一行程式碼時,可以省略花括號)。

ActionListener listener = e->JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);

小結
當使用Lambda 表達式取代匿名內部類別建立物件時,Lambda 表達式的程式碼區塊將會取代實作抽象方法的方法體,Lambda 就相當於一個匿名方法。

Lambda 表達式的組成部分

lambda 表達式由三個部分組成:

  • 形參列表。形參清單允許省略形參類型。如果形參列表中只有一個參數,可以省略形參列表的圓括號。

  • 箭頭(->)。英文短線和大於號。

  • 程式碼區塊。如果程式碼區塊只有一句,可以省略花括號。如果只有一條 return 語句,可以省略 return,lambda表達式會自動傳回這條語句的值。

註:
之所以可以省略形參列表是因為編譯器可以進行類型推斷,例如:

List<Dog> dogs1 = new ArrayList<Dog>();

List<Dog> dogs2 = new ArrayList<>();

上面使用菱形語法,可以省略尖括號裡面的東西,這就是類型推斷的作用
但是類型推論也不是萬能的,不是所有的都可以推斷出來的,所以有時候,還是要顯示的添加形參類型,例如:

先不要管這個代碼的具體作用。

BinaryOperator b = (x, y)->x*y;
//上面这句代码无法通过编译,下面是报错信息:无法将 * 运算符作用于 java.lang.Object 类型。
The operator * is undefined for the argument type(s) java.lang.Object, java.lang.Object
//添加参数类型,正确的代码。
BinaryOperator<Integer> b = (x, y)->x*y;

所以,型別推斷不是萬能的,如果編譯器無法推斷,那就是我們的錯誤,不要過度依賴編譯器。有時候,顯示的新增參數類型,還是很必要的,當然了,這需要去多練習。

函數式介面

前面了解了,Lambda 表達式可以取代匿名內部類,進而達到簡化程式碼,表達清晰的目的。那麼使用 Lambda 表示式的前提是什麼呢? -- 函數式介面

Lambda 表達式的類型,也稱為 目標類型 (Target Type),它必須是函數式介面(Functional Interface)。所謂函數式接口,指的就是:只包含一個抽象方法的接口。 (可以包含多個預設方法,靜態方法,但必須只有一個抽象方法)。
附註:Java 8 專門提供了一個註解:@FunctionalInterface。用於標註某個接口是函數式接口,這樣編譯時就會檢查,如果該接口含有多個抽象方法,編譯器就會報錯。

上面使用 Lambda 表达式来为按钮添加了监视器,可以看出来,Lambda 表达式 代替了 new ActionListener()对象。

所以 Lambda 的表达式就是被当成一个对象。

例如:

ActionListener listener = e->JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);

从上面这个例子中可以看出来,Lambda 表达式实现的是匿名方法–因此它只能实现特定函数式接口中的唯一方法。
所以 Lambda 表达式有下面两种限制:

Lambda 表达式的目标类型必须是明确的函数式接口。 Lambda 表达式只能为函数式接口创建对象。Lambda只能实现一个方法,因此它只能为含有一个抽象方法的接口(函数式接口)创建对象。 介绍几个 Java 中重要的函数接口

Java的Lambda表達式使用實例分析

从这种表可以看出来,抽象方法的名字反而不是最重要的了,重要的是参数和返回值。 因为在写 Lambda 表达式的时候,也不要使用 抽象方法名了。

上面使用几个简单的例子来说明上面接口的应用:

测试代码

import java.text.ParseException;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;

public class Test {
	public static void main(String[] args) throws ParseException {
		//Lambda 表达式中的构造器引用,用于简化代码。
		Creat<Dog> c = Dog::new;
		Dog dog = c.creat("小黑", 15);
		System.out.println(dog.toString());
		
		Predicate<String> predicate = (words)->{
			return words.length() > 20;
		};
		assert predicate.test("I love you yesterday and today!") : "长度小于20";
		assert !predicate.test("God bless you!") : "长度小于20";
		System.out.println("------------------------");
		
		Consumer<Dog> consumer = System.out::println;
		consumer.accept(dog);
		System.out.println("------------------------");

		Function<Dog, String> function = (dogObj)->{
			return dogObj.getName();
		};
		System.out.println(function.apply(dog));
		System.out.println("------------------------");
		
		Supplier<Dog> supplier = ()->{
			return new Dog("大黄", 4);
		};
		System.out.println(supplier.get());
		
		//一元操作符
		UnaryOperator<Boolean> unaryOperation = (flag)->{
			return !flag;
		};
		System.out.println(unaryOperation.apply(true));
		
		BinaryOperator<Integer> binaryOperator = (x, y)->x*y;
		int result = binaryOperator.apply(999, 9999);
		System.out.println(result);
	}
}

测试使用的实体类

public class Dog {
	private String name;
	private int age;
	
	public Dog(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 "Dog [name=" + name + ", age=" + age + "]";
	}
}

自定义函数式接口

@FunctionalInterface
public interface Creat<T> {
	public T creat(String name, int age);
}

运行截图就不放了,感兴趣的可以试一下。

说明
我这里直接使用 Lambda 创建了对象,然后调用了这个对象的方法(就是lambda 的代码块部分),真正使用的时候,都是直接传递 Lambda 表达式的,这种方法并不推荐,但是可以让我们很好的理解为什么? 可以看出来,Lambda 表达式的作用,最后还是需要调用 重写的抽象方法的,只不过使用表达更加清晰,简化了代码。

例如:

		List<Dog> dogs = new ArrayList<>();
		dogs.add(new Dog("大黄", 2));
		dogs.add(new Dog("小黑", 3));
		dogs.add(new Dog("小哈",1));
		//将行为像数据一样传递,使用集合的 forEach 方法来遍历集合,
		//参数可以是一个 Lambda 表达式。
		Consumer<? super Dog> con = (e)->{
			System.out.println(e);
		};
		dogs.forEach(con);
		System.out.println("--------------------------\n");
	
		//直接传递 Lambda 表达式,更加简洁
		dogs.forEach(e->System.out.println(e));
		System.out.println("--------------------------\n");

		//使用方法引用,进一步简化(可以看我的另一篇关于方法引用的博客)
		dogs.forEach(System.out::println);
		System.out.println("--------------------------\n");

		//使用 Lambda 对集合进行定制排序,按照年龄排序(从小到大)。
		dogs.sort((e1, e2)->e1.getAge()-e2.getAge());
		dogs.forEach(System.out::println);

可以看出来,通过使用 Lambda 表达式可以,极大的简化代码,更加方便的操作集合。值得一提的是:Lambda 表达式 和 Stream 的结合,可以拥有更加丰富的操作,这也是下一步学习的方向。

运行截图:

Java的Lambda表達式使用實例分析

以上是Java的Lambda表達式使用實例分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除