函數式接口
1. 函數式接口
1.1 概述
如果說一個接口內有且只有一個方法,而且該方法是一個缺省屬性爲public abstract方法,該接口可以稱之爲是一個函數式接口。
自定義函數式接口,還有系統中提供的函數式接口
Comparator<T> Runnable
可以直接理解JDK1.8的新特徵,Lambda表達式來使用。
Lambda表達式對比匿名內部類使用
1. 簡化了代碼結構
2. 節約了內存資源
3. 讓程序員更加關注,我要做什麼,而不是爲了做什麼需要完成什麼
1.2 @FunctionalInterface 使用
類似於
@Override
開啓代碼重寫格式嚴格檢查
/**
* 使用@FunctionalInterface檢查函數式接口格式問題
* 要求當前接口中有且只有一個缺省屬性爲public abstract的方法
*
* @author Anonymous 2020/3/11 9:55
*/
@FunctionalInterface
public interface FunctionalType {
void test();
}
1.3 使用自定義的函數式接口作爲方法的參數使用
package com.qfedu.a_functional;
/**
* 自定義函數式接口作爲方法的參數演示
*
* @author Anonymous 2020/3/11 9:55
*/
public class Demo1 {
public static void main(String[] args) {
/*
使用匿名內部類來完成函數式接口的使用,但是這種方式有悖於函數式接口的目的
Low
useFunctionalInterface(new FunctionalType() {
@Override
public void test() {
System.out.println("匿名內部類的匿名對象直接作爲方法的參數。");
}
});
int(*) (char **, int *)
C語言中的函數指針
如果是C語言中,這需要的參數是方法名
*/
useFunctionalInterface(() -> System.out.println("函數式接口 lambda表達式實現完成方法,實現目的"));
}
/**
* 使用一個函數式接口作爲方法的參數
*
* @param ft 函數式接口的實現類對象,或者說直接操作本質,直接傳入Lambda表達式
*/
public static void useFunctionalInterface(FunctionalType ft) {
ft.test();
}
}
代碼中使用函數式接口
1. 讓程序的目的性更強
2. 提供複用,普適性的價值
3. 節約資源
2. 函數式編程思想
2.1 Lambda延遲執行
2.1.1 日誌記錄
日誌是否保存會存在等級限制
演示一個根據不同的等級來記錄log日誌
要求:
等級 == 1 記錄log日誌,其他情況不記錄
package com.qfedu.b_lambda;
enum Level {
/**
* 枚舉測試
*/
HIGH, MIDDLE, LOWER
}
/**
* 日誌等級記錄操作
*
* @author Anonymous 2020/3/11 10:17
*/
public class Demo1 {
public static void main(String[] args) {
/*
這裏存問題:
"異常位置XXX," + "異常問題XXX," + "異常時間XXX" 字符疊加過程,會產生
5個字符串
這5個字符串如果Level.HIGH等級是有用的,但是除此之外是沒有任何作用的。
MIDDLE,LOWER是沒有必要進行字符串累加,存在資源浪費問題。
字符串的累加過程,需要經過Level判斷之後纔可以執行,避免沒有必要的性能浪
費。
這裏就可以使用Lambda表達式執行的延遲性,在沒有滿足level情況下,不去做字
符串累加過程。
這裏需要【函數式接口】
*/
log(Level.MIDDLE, "異常位置XXX," + "異常問題XXX," + "異常時間XXX");
}
/**
* 判斷等級是否需要記錄當前日誌信息
*
* @param level 等級,枚舉類型
* @param logMsg 需要記錄的日誌信息
*/
public static void log(Level level, String logMsg) {
// 判斷是否滿足枚舉類型 Level.HIGH要求
if (Level.HIGH == level) {
System.err.println(logMsg);
}
}
}
2.1.2 使用函數式接口提供日誌信息功能
這裏需要一個函數式接口,返回值類型是String類型,其他的無所謂。
package com.qfedu.b_lambda;
/**
* 提供返回值爲String類型方法的函數式接口
*
* @author Anonymous 2020/3/11 10:52
*/
@FunctionalInterface
public interface LogMessage {
/**
* 函數式接口中方法內容,該方法的返回值是String類型
*
* @return String類型返回值
*/
String returnLogMessage();
}
package com.qfedu.b_lambda;
/**
* 使用函數式接口完成Log日誌記錄問題
*
* @author Anonymous 2020/3/11 10:53
*/
public class Demo2 {
public static void main(String[] args) {
String msg1 = "異常位置XXX,";
String msg2 = "異常問題XXX,";
String msg3 = "異常時間XXX";
log(Level.LOWER, () -> {
System.out.println("Lambda表達式執行!!!");
return msg1 + msg2 + msg3;
});
}
/**
* 根據日誌等級Level來確定是否需要記錄日誌
*
* @param level Level枚舉類型,有三個數據 HIGH MIDDLE LOWER
* @param lm LogMessage函數式接口做方法的參數
*/
public static void log(Level level, LogMessage lm) {
/*
發現當Level等級爲HIGH,執行對應的lm.returnLogMessage();
Level等級不是HIGH不執行對應的方法。
Lambda執行延遲問題不是Lambda效率執行慢,而是在執行之前多了一個判斷
是在判斷之後纔可以執行對應的代碼。
不執行代碼字符串不會產生拼接導致的資源浪費問題,從而提高效率。
*/
if (Level.HIGH == level) {
// 通過函數式接口獲取調用對應的returnLogMessage()方法
System.err.println(lm.returnLogMessage());
}
}
public static void testEnum(int level) {
if (Level.HIGH.getStatus() == level) {
}
}
}
2.2 Lambda作爲方法參數和返回值
參數演示:
Runnable接口
package com.qfedu.b_lambda;
/**
* Runnable接口函數式接口使用,作爲方法的參數
*
* @author Anonymous 2020/3/11 11:18
*/
public class Demo3 {
public static void main(String[] args) {
// 匿名內部類來完成對應當前Runnable接口實現類對象使用,作爲Thread構造方法參數
// low
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("線程代碼");
}
}).start();
// Lambda表達式直接作爲方法的參數
Thread thread = new Thread(() -> {
System.out.println("線程執行需要時間");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程執行");
}
, "線程");
thread.start();
}
}
Java中提供的比較接口Comparator<T>
利用一些返回值作爲方法中操作的調節
public interface Comparator<T> {
int compare(T o1, T o2);
}
package com.qfedu.b_lambda;
import java.util.Arrays;
import java.util.Comparator;
/**
* Lambda表示完成函數式接口,利用返回值作爲其他操作所需的數據
*
* @author Anonymous 2020/3/11 11:25
*/
public class Demo4 {
public static void main(String[] args) {
/*
字符串排序默認是一個字典順序
*/
String[] arr = {"eeeeeeee", "2a","dddd", "1bb", "ccccccc", "3fffff"};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
System.out.println("-----------------------------------------");
// 利用一個函數式接口完成的方法,利用方法的返回值作爲當前sort方法運行
// 所需參數,該參數用於比較規則約束
Arrays.sort(arr, stringComparator());
System.out.println(Arrays.toString(arr));
System.out.println("-----------------------------------------");
Arrays.sort(arr, (a, b) -> a.length() - b.length());
Arrays.sort(arr, Comparator.comparingInt(String::length));
/*
Comparator.comparingInt(String::length)
Comparator:接口
comparingInt:按照Int類型方式比較判斷
String:表示比較的類型是String,比較什麼類型用什麼類型
length:按照String類型的哪一種方式比較
*/
System.out.println(Arrays.toString(arr));
}
/**
* 按照字符串長度排序,返回值類型是一個Comparator<String>接口
* 這裏需要完成Comparator接口中的compare方法
*
* @return 已經完成方法體的Comparator接口,並且數據類型是String類型
*/
public static Comparator<String> stringComparator() {
/*
public interface Comparator<T> {
int compare(T o1, T o2);
}
*/
return (a, b) -> b.length() - a.length();
}
}
3. Java中提供的常用函數式接口
3.1 JDK常用函數式接口概述
java.util.function包名 。提供了很多函數式接口
規範了一些操作,提升了開發效率,更加專注於目的性!!!
Supplier<T> 生產者, 返回一個指定類型的數據
Consumer<T> 消費者, 消耗一個指定類型的數據
Predicate<T> 判斷調節,過濾使用
Function<T,R> 類型轉換,根據你指定的類型T, 轉換成對應類型R
3.2 Supplier 生產者,返回一個指定的數據類型
java.util.function.Supplier<T>
有且只有一個方法
T get();
不需要參數,返回指定T類型數據
什麼都不喫,擠的都是輸出。。。
package com.qfedu.c_supplier;
import com.qfedu.b_lambda.Level;
import java.util.function.Supplier;
/**
* Supplier函數式接口演示
*
* @author Anonymous 2020/3/11 14:44
*/
public class Demo1 {
public static void main(String[] args) {
String msg1 = "異常位置XXX,";
String msg2 = "異常問題XXX,";
String msg3 = "異常時間XXX";
/*
這裏需要的是一個函數式接口,直接傳入一個lambda表達式
*/
log(Level.HIGH, () -> {
System.out.println("Lambda表達式執行!!!");
return msg1 + msg2 + msg3;
});
/*
Lambda表達式優化
*/
log(Level.HIGH, () -> msg1 + msg2 + msg3);
}
/**
* 根據日誌等級Level來確定是否需要記錄日誌
*
* @param level Level枚舉類型,有三個數據 HIGH MIDDLE LOWER
* @param supplier Supplier函數式接口,利用T get() 完成提供數據操作
*/
public static void log(Level level, Supplier<String> supplier) {
/*
Supplier函數式接口利用get方法,提供對應的返回指定String類型數據的操作
*/
if (Level.HIGH == level) {
// 通過函數式接口獲取調用對應的returnLogMessage()方法
System.err.println(supplier.get());
}
}
}
找出數組中最大值所在下標位置
package com.qfedu.c_supplier;
import java.util.function.Supplier;
/**
* 利用函數式接口Supplier其中get方法,找出數組中最大值下標位置
*
* @author Anonymous 2020/3/11 14:54
*/
public class Demo2 {
public static void main(String[] args) {
int[] array = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
/*
getMax方法需要的參數是一個Supplier函數式接口,這裏可以使用Lambda表達式完成
*/
int max = getMax(() -> {
int index = 0;
for (int i = 1; i < array.length; i++) {
if (array[index] < array[i]) {
index = i;
}
}
return index;
});
System.out.println(max);
}
/**
* 利用函數式接口完成一個getMax,提供一個操作思想
*
* @param sup Supplier函數式接口
* @return 最大值下標位置
*/
public static int getMax(Supplier<Integer> sup) {
return sup.get();
}
}
引出滿足更多普適性代碼的函數式接口使用方式
package com.qfedu.c_supplier;
import java.util.function.Supplier;
/**
* 演示Supplier函數式接口使用,對於方法的拓展能力和
* 普適性滿足
*
* @author Anonymous 2020/3/11 15:06
*/
public class Demo3 {
public static void main(String[] args) {
Integer[] array = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
Integer max = getMax(array, () -> {
Integer temp = array[0];
for (int i = 1; i < array.length; i++) {
if (temp < array[i]) {
temp = array[i];
}
}
return temp;
});
System.out.println(max);
System.out.println("-------------------------------------------");
Person[] persons = {new Person(1, "騷磊", 16),
new Person(2, "老黑", 66),
new Person(3, "老付", 36),
new Person(4, "老高", 26),
new Person(5, "污雲", 96),
new Person(6, "帥棟", 106)
};
Person max1 = getMax(persons, () -> {
Person p = persons[0];
for (int i = 1; i < persons.length; i++) {
if (p.getAge() < persons[i].getAge()) {
p = persons[i];
}
}
return p;
});
System.out.println(max1);
}
/**
* 找出任意類型數組中最大數據
*
* @param arr 任意類型數組,同時約束當前泛型的使用規則
* @param sup Supplier函數式接口,要利用get方法,返回指定T類型數據
* @param <T> 泛型
* @return 和數組類型一致的數據
*/
public static <T> T getMax(T[] arr, Supplier<T> sup) {
return sup.get();
}
}
3.3 Consumer消費者,處理數據
Consumer<T>
操作使用的方式是
void accept(T t);
根據接口指定的數據類型接收對應數據,進行處理和消費,對外沒有任何的返回
至於處理的過程,展示,處理,計算。。。
package com.qfedu.d_consumer;
import java.util.function.Consumer;
/**
* 使用Consumer處理數據
*
* @author Anonymous 2020/3/11 15:24
*/
public class Demo1 {
public static void main(String[] args) {
// 該方法需要的參數是一個String類型,同時使用Consumer接口處理數據
// 因爲Consumer接口是一個函數式接口,可以使用Lambda表達式
testConsumer("宮保雞丁,番茄牛腩,醬牛肉,黃燜雞米飯", (str) -> {
String[] split = str.split(",");
for (String s : split) {
System.out.println(s);
}
});
}
/**
* 給予當前方法一個String類型,通過Consumer函數式接口中的accept方法完成對應
* 字符串處理
*
* @param str String類型字符串
* @param consumer Consumer處理數據的函數式接口
*/
public static void testConsumer(String str, Consumer<String> consumer) {
consumer.accept(str);
}
}
andThen
package com.qfedu.d_consumer;
import java.util.function.Consumer;
/**
* Consumer接口andThen使用
* 需要兩個Consumer接口,兩個Consumer接口進行組合處理,對數據進行消費
*
* andThen(Consumer<T> con)
* 調用格式
* con1.andThen(con2).accept(T t);
* 等價於一下操作
* con1.accept(t);
* con2.accept(t);
*
* @author Anonymous 2020/3/11 15:57
*/
public class Demo2 {
public static void main(String[] args) {
/*
該方法需要兩個Consumer函數式接口,這裏可以使用兩個Lambda表達式操作
*/
testAndThen("鄭州加油!!!中國加油!!!",
(str) -> System.out.println(str)
,
(str) -> System.err.println(str)
);
}
/**
* 使用兩個Consumer消費者方式處理str數據,首先是con1處理,再來con2處理
*
* @param str 需要處理的String類型數據
* @param con1 Consumer<String> 處理String類型函數式接口
* @param con2 Consumer<String> 處理String類型函數式接口
*/
public static void testAndThen(String str, Consumer<String> con1, Consumer<String> con2) {
/*
con1.accept(str);
con2.accept(str);
允許組合拳
con1.andThen(con2).andThen(con1).andThen(con2).andThen(con1).andThen(con2).accept(str);
*/
con1.andThen(con2).accept(str);
}
}
3.4 Predicate 判斷數據是否合適,返回true/false
Predicate<T>一般用於條件判斷,過濾數據的方法
函數式接口中指定的方法
boolean test(T t);
處理T類型數據,返回boolean true / false
package com.qfedu.e_predicate;
import java.util.function.Predicate;
/**
* 演示Predicate<T>基本使用
* boolean test(T t)
*
* @author Anonymous 2020/3/11 16:11
*/
public class Demo1 {
public static void main(String[] args) {
// Predicate函數式接口,使用Lambda表達式作爲方法的參數
boolean b = testPredicate("鄭州奧力給!!!中國奧力給!!!",
(str) -> {
return str.contains("加油");
});
System.out.println("ret : " + b);
System.out.println("---------------------------");
/*
優化Lambda表達式,
因爲是一個參數,小括號可以省略
就一行代碼,大括號可以省略
return也可以省略
*/
testPredicate("鄭州奧力給!!!中國奧力給!!!", str -> str.contains("加油"));
}
/**
* 使用Predicate函數式接口利用boolean test(T t)對於當前數據進行判斷操作,
* 返回boolean類型數據
*
* @param str 需要進行判斷數據的String類型字符串
* @param pre 處理使用Predicate函數式接口
* @return 判斷接口是否滿足要求,滿足返回true,不滿足返回false
*/
public static boolean testPredicate(String str, Predicate<String> pre) {
return pre.test(str);
}
}
and 與
package com.qfedu.e_predicate;
import java.util.function.Predicate;
/**
* Predicate and使用
* default修飾方法add(Predicate<T> pre)
* and就是邏輯運算符裏面的 &&
* 同真爲真,有假【即】假
* 需要對兩個Predicate進行判斷處理
*
* 例如:
* pre1.test(str) && pre2.test(srt);
* ==> pre1.and(pre2).test(str);
*
* @author Anonymous 2020/3/11 16:19
*/
public class Demo2 {
public static void main(String[] args) {
/*
這裏需要量Predicate接口,使用Lambda
*/
boolean ret = testAnd("趕緊復工吧,不要搞事情了!!!",
str -> str.length() > 5,
str -> str.startsWith("趕緊"));
System.out.println(ret);
}
/**
* 組合判斷
*
* @param str 需要判斷的字符串
* @param pre1 判斷方式1
* @param pre2 判斷方式2
* @return 處理結果 true, false
*/
public static boolean testAnd(String str, Predicate<String> pre1, Predicate<String> pre2) {
// return pre1.test(str) && pre2.test(str)
return pre1.and(pre2).test(str);
}
}
or 或
package com.qfedu.e_predicate;
import java.util.function.Predicate;
/**
* Predicate or演示
*
* @author Anonymous 2020/3/11 16:32
*/
public class Demo3 {
public static void main(String[] args) {
boolean ret = testOr("國家之強大,國外人羨慕不得~~",
str -> str.length() < 10,
str -> str.contains("國家"));
System.out.println(ret);
}
/**
* or 組合判斷
*
* @param str 需要判斷的字符串
* @param pre1 判斷方式1
* @param pre2 判斷方式2
* @return 處理結果 true, false
*/
public static boolean testOr(String str, Predicate<String> pre1, Predicate<String> pre2) {
// return pre1.test(str) || pre2.test(str);
return pre1.or(pre2).test(str);
}
}
negate 非
package com.qfedu.e_predicate;
import java.util.function.Predicate;
/**
* Predicate negate()操作
*
* @author Anonymous 2020/3/11 16:36
*/
public class Demo4 {
public static void main(String[] args) {
boolean ret = testNegate("疫情總會過去的!!!",
str -> str.length() < 5);
System.out.println(ret);
}
/**
* negate操作
*
* @param str 字符串
* @param pre Predicate函數式接口
* @return 處理結果
*/
public static boolean testNegate(String str, Predicate<String> pre) {
// return !pre.test(str);
return pre.negate().test(str);
}
}
ArrayList中使用Predicate刪除指定數據
package com.qfedu.e_predicate;
import com.qfedu.c_supplier.Person;
import java.util.ArrayList;
/**
* ArrayList,使用Predicate作爲條件約束刪除對應的數據
*
* @author Anonymous 2020/3/11 16:41
*/
public class Demo5 {
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<>();
list.add(new Person(1, "騷磊", 16));
list.add(new Person(2, "老黑", 66));
list.add(new Person(3, "老付", 36));
list.add(new Person(4, "老高", 26));
list.add(new Person(5, "污雲", 96));
list.add(new Person(6, "帥棟", 96));
// 直接安排Predicate函數式接口來約束對應的條件,進行刪除操作
// 代碼的閱讀性,普適性提升
// 以及代碼的冗餘程度降低
list.removeIf(person -> person.getAge() > 40 && person.getId() > 3);
System.out.println(list);
}
}
3.5 Function<T,R> 類型轉換
使用R apply(T t)
轉換指定類型T到R
package com.qfedu.f_function;
import com.qfedu.c_supplier.Person;
import java.util.function.Function;
/**
* Function<T, R> 函數式接口
* R apply(T)
*
* @author Anonymous 2020/3/11 16:50
*/
public class Demo1 {
public static void main(String[] args) {
// Integer類型轉換成一個String
String change = change(10, i -> i + "");
System.out.println(change);
// 利用函數式接口處理一個String類型,轉換成對應的Person類型
Person person1 = change("1,騷磊,16", str -> {
String[] split = str.split(",");
Person person = new Person();
person.setId(Integer.parseInt(split[0]));
person.setName(split[1]);
person.setAge(Integer.parseInt(split[2]));
return person;
});
System.out.println(person1);
}
/**
* 轉換格式的方法,要求數據從Integer類型轉換到指定的String類型
*
* @param i 需要轉換的Integer類型
* @param fun 轉換使用的Function函數式接口
* @return 返回值的是String類型
*/
public static String change(Integer i, Function<Integer, String> fun) {
return fun.apply(i);
}
public static Person change(String str, Function<String, Person> fun) {
return fun.apply(str);
}
}
andThen
package com.qfedu.f_function;
import java.util.function.Function;
/**
* Function<T, R>
* default修飾andThen方法使用
*
* @author Anonymous 2020/3/11 17:01
*/
public class Demo2 {
public static void main(String[] args) {
String s = testAndThen(10,
i -> i + "",
i -> i + "測試");
System.out.println(s);
}
/**
* 兩次轉換過程
*
* @param i 需要處理的類型
* @param fun1 Function函數接口
* @param fun2 Function函數接口
* @return String類型
*/
public static String testAndThen(int i, Function<Integer, String> fun1, Function<String, String> fun2) {
// andThen使用,最後apply方法參數類型是fun1要求的轉換參數類型
return fun1.andThen(fun2).apply(i);
}
}