JDK1.8新特性:Lambda表示式語法和內建函式式介面
前言:Lambda 是一個匿名函式,我們可以把 Lambda表示式理解為是一段可以傳遞的程式碼(將程式碼像資料一樣進行傳遞)。可以寫出更簡潔、更靈活的程式碼。作為一種更緊湊的程式碼風格,使Java的語言表達能力得到了提升。
首先我們來看下一個簡單從匿名類到Lambda的例子,體會下Lambda的特點
在java8以前我們通過實行Runable介面建立執行緒
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello Runnable Run");
}
}).start();
利用java8的Lambad語法我們可以更方便的實現相同的功能
new Thread(() -> System.out.println("Hello Runnable Run")).start();
例2:在這裡我們定義一個需求:通過年齡或者工資過濾員工資訊
基於此業務場景我們先定義一個員工物件
package cq.java8.lambda;
public class Employ {
private Integer age; //年齡
private Double salary; //工資
private String name; //姓名
public Employ() {
super();
}
public Employ(Integer age, Double salary, String name) {
super();
this.age = age;
this.salary = salary;
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Employ [age=" + age + ", salary=" + salary + ", name=" + name + "]";
}
}
我們簡單運用策略模式實現此類業務需求,首先定義一個公共的介面
//定義一個介面
public interface EmployStrategy {
//定義一個篩選方法
public boolean filterEmploy(Employ employ);
}
實現上面的公共介面過濾年齡大於25歲的員工
//定義一個根據年齡篩選的實現類
public class EmployFilterByAge implements EmployStrategy{
@Override
public boolean filterEmploy(Employ employ) {
return employ.getAge() > 25 ? true : false;
}
}
實現上面的公共介面實現年齡工資大於6000的員工
//定義一個根據工資篩選的實現類
public class EmployFilterBySalary implements EmployStrategy{
@Override
public boolean filterEmploy(Employ employ) {
return employ.getSalary() > 6000 ? true : false;
}
}
功能實現
public class TestLambda {
List<Employ> employs = Arrays.asList(
new Employ(18, 5555.55, "張三"),
new Employ(22, 6666.66, "李四"),
new Employ(33, 3333.33, "王五"),
new Employ(44, 9999.99, "趙六"),
new Employ(55, 8888.88, "田七"));
//定義過濾員工返回新員工集合方法
public List<Employ> getEmployByFilter(List<Employ> employs, EmployStrategy employStrategy){
List<Employ> newEmploys = new ArrayList<Employ>();
for(Employ employ : employs) {
if(employStrategy.filterEmploy(employ)) {
newEmploys.add(employ);
}
}
return newEmploys;
}
//通過策略模式實現
@Test
public void test() {
//根據年齡過濾
List<Employ> ageFilterEmploys = getEmployByFilter(employs, new EmployFilterByAge());
System.out.println("年齡大於25的員工:");
for (Employ e : ageFilterEmploys) {
System.out.println(e);
}
//根據工資過濾
List<Employ> salaryFilterEmploys = getEmployByFilter(employs, new EmployFilterBySalary());
System.out.println("工資大於6000的員工:");
for (Employ e : salaryFilterEmploys) {
System.out.println(e);
}
}
}
執行結果如下:
年齡大於25的員工:
Employ [age=33, salary=3333.33, name=王五]
Employ [age=44, salary=9999.99, name=趙六]
Employ [age=55, salary=8888.88, name=田七]
工資大於6000的員工:
Employ [age=22, salary=6666.66, name=李四]
Employ [age=44, salary=9999.99, name=趙六]
Employ [age=55, salary=8888.88, name=田七]
此實現方式進行了解耦,弊端就是每次新增需求需要加新的介面實現。當然我們也可以用匿名內部類的方式實現,當然我們也可以通過匿名內部類的方式實現,如下:
//通過匿名內部類實現
@Test
public void test2() {
//根據年齡過濾
List<Employ> ageFilterEmploys = getEmployByFilter(employs, new EmployStrategy() {
@Override
public boolean filterEmploy(Employ employ) {
return employ.getAge() > 25 ? true : false;
}
});
System.out.println("年齡大於25的員工:");
for (Employ e : ageFilterEmploys) {
System.out.println(e);
}
//根據年齡過濾
List<Employ> salaryFilterEmploys = getEmployByFilter(employs, new EmployStrategy() {
@Override
public boolean filterEmploy(Employ employ) {
return employ.getSalary() > 6000 ? true : false;
}
});
System.out.println("工資大於6000的員工:");
for (Employ e : salaryFilterEmploys) {
System.out.println(e);
}
}
從第一個建立執行緒的例子中,延用相同的思路我們利用java8的lambda語法實現上述功能如下(介面還是使用EmployStrategy)
//Lambda表示式實現
@Test
public void test3() {
List<Employ> ageFilterEmploys = getEmployByFilter(employs, e -> e.getAge() > 25);
System.out.println("年齡大於25的員工:");
for (Employ e : ageFilterEmploys) {
System.out.println(e);
}
List<Employ> salaryFilterEmploys = getEmployByFilter(employs, e -> e.getSalary() > 6000);
System.out.println("年齡大於25的員工:");
for (Employ e : salaryFilterEmploys) {
System.out.println(e);
}
}
從上文實現員工過濾的例項中我們可以看到Lambda表單式可以大大簡化程式碼。在這裡我們也可以對Lambda表示式有個簡單的瞭解和定義:可以將lambda表示式定義為一種 簡潔、可傳遞的匿名函式,首先我們需要明確lambda表示式本質上是一個函式,雖然它不屬於某個特定的類,但具備引數列表、函式主體、返回型別,以及能夠丟擲異常;其次它是匿名的,lambda表示式沒有具體的函式名稱;lambda表示式可以像引數一樣進行傳遞,從而極大的簡化程式碼的編寫。上文所闡述的其實就是在告訴大家,我們為什麼要使用Lambda表示式,接下來我們詳細瞭解下Lambda語法。
1.表示式介紹
Lambda表示式在Java語言中引入了新的語法元素和操作符。這個操作符為 “ ->” , 該操作符被稱為 Lambda 操作符或剪頭操作符。它將 Lambda 分為兩個部分:
左側: 指定了 Lambda 表示式需要的所有引數
右側: 指定了 Lambda 體,即 Lambda 表示式要執行的功能。
2.語法介紹
(2.1)語法格式一:無參,無返回值,Lambda體只需要一條語句
語法格式及案例:() -> System.out.println("無參無返回值Lambda表示式")
@Test
public void test4() {
new Thread(() -> System.out.println("無參無返回值Lambda表示式")).start();
}
(2.2)語法格式二:一個引數,無返回值,此時引數的小括號可省略
語法格式及案例:args -> System.out.println(args)
@Test
public void test5() {
Consumer<String> con = x -> System.out.println(x);
con.accept("新年好");
}
(2.3)語法格式三:一個引數,有返回值,當Lambda體只有一條語句是return與大括號可省略
語法格式及案例:(args) -> return args引數處理後的結果
@Test
public void test6() {
//接收一個引數返回其2的倍數
Function<Integer, Integer> fun = (x) -> 2*x;
fun.apply(5);
}
(2.4)語法格式四:兩個引數,無返回值,當引數的資料型別可由編譯器通過上下文推薦出來時,可省
語法格式及案例:(args1,args2) -> {兩個引數處理,無返回值}
@Test
public void test7() {
//求兩個數的和並列印
BiConsumer<Integer, Integer> biConsumer = (x, y) -> System.out.println(x+y);
biConsumer.accept(10, 20);
}
(2.5)語法格式五:兩個引數,有返回值,Lambda體只有1條語句
語法格式及案例:(args1, args2) -> return args1和args2處理後返回
@Test
public void test8() {
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
int r = com.compare(5, 10);
System.out.println(r);
}
(2.6)語法格式五:兩個引數,有返回值,Lambda體有多條語句
語法格式及案例:
(args1, args2) -> { System.out.println("輸入兩個引數"); return args1和args2處理後返回 }
@Test
public void test9() {
Comparator<Integer> com = (x, y) ->{
System.out.println("輸入兩個引數");
return Integer.compare(x, y);
}
int r = com.compare(5, 10);
System.out.println(r);
}
3.JDK1.8提供的內建函式式介面
上文中我們定義了EmployStrategy 介面,並且只包含一個抽象方法的介面,可以稱之為函式式介面,我們可以在任意的函式式介面上使用@FunctionalInterface註解,這樣做可以檢查它是否是一個函式式介面,我們可以通過Lambda表示式建立該介面物件。
Java 內建四大核心函式式介面
介面型別 | 函式式介面 | 引數型別 | 返回值 | 用途 |
---|---|---|---|---|
消費型 | Consumer<T> | T | 無 | 對型別為T的物件應用操作,方法:void accept(T t) |
供給型 | Supplier<T> | 無 | T | 返回型別為T的物件,方法:T get() |
函式型 | Function<T,R> | T | R | 對型別為T的應用操作,並返回結果。結果是R型別的物件,方法R apply(T t) |
斷言型 | Predicate<T> | T | boolean | 確定物件T是否滿足某個約束條件並返回boolean值,方法boolean test(T t) |
其他函式式介面
介面型別 | 函式式介面 | 引數型別 | 返回值 | 用途 |
---|---|---|---|---|
函式型 | BiFunction<T,U,R> | T,U | R | 對型別為T,U的物件應用操作,返回R型別的結果方法:R apply(T t,U u) |
函式型 | UnaryOperator<T> | T | T | 對型別為T的物件應用操作(一元運算),返回T型別的結果,方法:T apply(T t),是Function的子類 |
函式型 | BinaryOperator<T> | T,T | T | 對型別為T的應用操作(二元運算),並返回T型別結果。結果是R型別的物件,方法R apply(T t1, T t2),是BiFunction的子類 |
消費型 | BiConsumer<T,U> | T,U | 無 | 對型別為T,U的物件應用操作,方法void accept(T t, U u) |
在這裡我們著重介紹四大核心函式式介面的使用
(3.1) Consumer<T> 方法 void accept(T t)
在這裡我們定義一個需求:執行一段業務邏輯無引數無返回值,用此內建函式式介面實現如下:
public class TestConsumer {
//定義一個花錢的消費方法,引數:錢、消費函式介面
public void costMoney(double money, Consumer<Double> con) {
//花錢
con.accept(money);
}
@Test
public void test() {
//引數m,無返回值
costMoney(1000, m -> System.out.println("花了"+m+"元錢,很開心!"));
}
}
(3.2) Supplier<T> 方法 T get()
在這裡我們定義一個需求作為例項:獲取指定數量的隨機數,並放入集合,用此內建函式式介面實現如下:
public class TestSupplier {
//獲取指定數量的隨機數,並放入集合返回
public List<Integer> getRandList(int num, Supplier<Integer> sup){
List<Integer> randList = new ArrayList<>();
for(int i = 0; i < num; i++) {
//獲取隨機數並放入集合
randList.add(sup.get());
}
return randList;
}
@Test
public void test() {
List<Integer> randList = getRandList(10, () -> (int)(Math.random()*100));
System.out.println(randList);
//列印結果:[76, 43, 23, 12, 42, 96, 84, 82, 4, 93]
}
}
(3.3) Function<T, R> 方法 R apply(T t)
在這裡我們定義一個需求作為例項:去除字串空格,用此內建函式式介面實現如下:
public class TestFunction {
//定義獲取字串方法
public String getStr(String string, Function<String, String> fun) {
return fun.apply(string);
}
@Test
public void test() {
String string = " 中國人民萬歲,中華人名共和國萬歲";
//去掉空格
string = getStr(string, (t) -> t.trim());
System.out.println(string);
//列印結果:中國人民萬歲,中華人名共和國萬歲
}
}
(3.4) Predicate<T> 方法 boolean test(T t)
在這裡我們定義一個需求:從集合中獲取部分滿足條件的元素放入新集合並返回,用此內建函式式介面實現如下:
public class TestPredicate {
//從集合中獲取部分滿足條件的元素放入新集合並返回
public List<Integer> getList(List<Integer> list, Predicate<Integer> pre){
List<Integer> listNew = new ArrayList<>();
for(Integer v : list) {
if(pre.test(v)) {
listNew.add(v);
}
}
return listNew;
}
@Test
public void test() {
List<Integer> list = Arrays.asList(39,8,12,99,22);
//獲取值小於30,並返回新集合
List<Integer> listNew = getList(list, (value) -> value < 30);
System.out.println("小於30:"+listNew);
//獲取值大於40,並返回新集合
listNew = getList(list, value -> value > 40);
System.out.println("大於40:"+listNew);
//列印結果
//小於30:[8, 12, 22]
//大於40:[99]
}
}
相關文章
- JDK1.8新特性--Lambda表示式JDK
- jdk1.8新特性:Lambda表示式JDK
- JDK1.8新特性之Lambda表示式JDK
- JDK1.8新特性之Lambda表示式()->JDK
- java8新特性之函式式介面、lambda表示式、介面的預設方法、方法和建構函式的引用Java函式
- JDK1.8的新特性之Lambda表示式JDK
- jdk1.8 新特性之 如何寫lambda表示式JDK
- ?Java8新特性之Lambda表示式,函式式介面,方法引用和default關鍵字Java函式
- java-反射,介面新特性,Lambda表示式Java反射
- kotlin 函式和 Lambda 表示式Kotlin函式
- Lambda表示式入門--函數語言程式設計與函式式介面函數程式設計函式
- 5.函式和lambda表示式函式
- JDK1.8 之Lambda表示式JDK
- JDK1.8之lambda表示式JDK
- 【Python】python map()函式和lambda表示式Python函式
- JDK 1.8 新特性之Lambda表示式JDK
- Java 8新特性(一):Lambda表示式Java
- .NET3.5新特性,Lambda表示式
- 從五大語言看函式和lambda表示式函式
- JDK1.8之內建函式式介面(方法引用的實現)JDK函式
- Python函式與lambda 表示式(匿名函式)Python函式
- [Java學習筆記]JDK1.8新特性學習(一)Lambda表示式Java筆記JDK
- jdk1.8 lambda表示式入門JDK
- 【Java8新特性】Lambda表示式基礎語法,都在這兒了!!Java
- Java8新特性(一)-Lambda表示式Java
- JDK新特性-Lambda表示式的神操作JDK
- Java8新特性(1):Lambda表示式Java
- java8 新特性之Lambda 表示式Java
- jdk1.8-Lambda函式表示式JDK函式
- 課時21:函式:lambda表示式函式
- jdk1.8Lambda函式表示式JDK函式
- 初識Lambda表示式(匿名函式)函式
- JDK1.8及以上的Lambda表示式JDK
- JDK1.8 Lambda 表示式的學習JDK
- Lambda表示式基本語法與應用
- JDK8新特性之函式式介面JDK函式
- Java8的新特性--函式式介面Java函式
- java8 新特性之函式式介面Java函式