Java 中的 Function 接口案例講解
Function 是 Java 8 引入的一個核心函數(shù)式接口,屬于 java.util.function 包。它代表一個"接受一個參數(shù)并產(chǎn)生結(jié)果"的函數(shù)。
1. Function 的基本概念
1.1. 定義
T:輸入類型(參數(shù)類型)
R:結(jié)果類型(返回類型)
唯一抽象方法:R apply(T t)
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
// 還包含兩個默認方法(后面會講解)
default <V> Function<V, R> compose(Function<? super V, ? extends T> before)
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after)
}1.2. 基本用法示例
// 1. 定義一個將字符串轉(zhuǎn)為長度的函數(shù)
Function<String, Integer> lengthFunction = s -> s.length();
System.out.println(lengthFunction.apply("Hello")); // 輸出 5
// 2. 定義將數(shù)字轉(zhuǎn)為字符串的函數(shù)
Function<Integer, String> stringify = num -> "Number: " + num;
System.out.println(stringify.apply(42)); // 輸出 "Number: 42"2. Function 的方法詳解
// 1. apply(T t)
// 核心方法,執(zhí)行函數(shù)轉(zhuǎn)換:
Function<String, String> upperCase = s -> s.toUpperCase();
String result = upperCase.apply("hello"); // 返回 "HELLO"
-------------------------------------------------------------------------
// 2. compose(Function before)
// 組合函數(shù),先執(zhí)行 before 函數(shù),再執(zhí)行當(dāng)前函數(shù):
Function<Integer, String> intToStr = i -> "Value: " + i;
Function<String, Integer> strToInt = s -> Integer.parseInt(s);
// 先執(zhí)行 strToInt,再執(zhí)行 intToStr
Function<String, String> composed = intToStr.compose(strToInt);
System.out.println(composed.apply("123")); // 輸出 "Value: 123"
-------------------------------------------------------------------------
// 3. andThen(Function after)
// 組合函數(shù),先執(zhí)行當(dāng)前函數(shù),再執(zhí)行 after 函數(shù):
Function<String, Integer> parse = Integer::parseInt;
Function<Integer, Integer> square = i -> i * i;
// 先執(zhí)行 parse,再執(zhí)行 square
Function<String, Integer> parseThenSquare = parse.andThen(square);
System.out.println(parseThenSquare.apply("5")); // 輸出 25
-------------------------------------------------------------------------
// 4. identity()
// 靜態(tài)方法,返回一個總是返回其輸入?yún)?shù)的函數(shù):
Function<String, String> identity = Function.identity();
System.out.println(identity.apply("Same")); // 輸出 "Same"
-------------------------------------------------------------------------3. Function 的變體
Java 還提供了 Function 的一些特化版本:
3.1. 基本類型特化
接口 方法簽名 等價于
IntFunction<R> R apply(int value) Function<Integer, R>
DoubleFunction<R> R apply(double value) Function<Double, R>
LongFunction<R> R apply(long value) Function<Long, R>
IntFunction<String> intToString = i -> "Num: " + i; System.out.println(intToString.apply(10)); // 輸出 "Num: 10"
3.2. 多參數(shù)變體
接口 方法簽名
BiFunction<T,U,R> R apply(T t, U u)
BiFunction<String, String, String> concat = (s1, s2) -> s1 + s2;
System.out.println(concat.apply("Hello", "World")); // 輸出 HelloWorld4. 實際應(yīng)用
4.1. Stream API 中的 map 操作
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 將名字轉(zhuǎn)為大寫
List<String> upperNames = names.stream()
.map(name -> name.toUpperCase()) // 這里使用Function
.collect(Collectors.toList());4.2. 策略模式實現(xiàn)
// 定義不同的處理策略
Map<String, Function<String, String>> processors = new HashMap<>();
processors.put("upper", String::toUpperCase);
processors.put("lower", String::toLowerCase);
processors.put("reverse", s -> new StringBuilder(s).reverse().toString());
// 使用策略
String input = "Hello";
String output = processors.get("upper").apply(input); // 輸出 "HELLO"4.3. 方法鏈式處理
Function<Integer, Integer> times2 = x -> x * 2; Function<Integer, Integer> minus1 = x -> x - 1; // 組合函數(shù):先乘2再減1 Function<Integer, Integer> chain = times2.andThen(minus1); System.out.println(chain.apply(10)); // 輸出 19
4.4. 性能考慮
對象創(chuàng)建開銷:每次使用 lambda 表達式都會創(chuàng)建一個新對象
自動裝箱問題:對于基本類型,使用特化接口(如 IntFunction)更高效
JIT 優(yōu)化:HotSpot 虛擬機會對 lambda 進行優(yōu)化
5. 高級使用
5.1. 柯里化(Currying)
5.1.1. 示例
// 將二元函數(shù)轉(zhuǎn)換為一元函數(shù)鏈 Function<Integer, Function<Integer, Integer>> adder = a -> b -> a + b; Function<Integer, Integer> add5 = adder.apply(5); System.out.println(add5.apply(3)); // 輸出 8
5.1.2. 概念
柯里化的核心思想是:將一個接受多個參數(shù)的函數(shù),轉(zhuǎn)換成一系列使用一個參數(shù)的函數(shù)。
原始加法函數(shù)
BiFunction<Integer, Integer, Integer> normalAdd = (a, b) -> a + b;
柯里化后的加法函數(shù)
BiFunction<Integer, Integer, Integer> normalAdd = (a, b) -> a + b;
5.1.3. 示例解析
<code>Function<Integer, Function<Integer, Integer>> adder = a -> b -> a + b; Function<Integer, Integer> add5 = adder.apply(5); System.out.println(add5.apply(3)); // 輸出 8 ------------------------------------------------------------------------ 1.定義柯里化函數(shù): Function<Integer, Function<Integer, Integer>> adder = a -> b -> a + b; // 這是一個函數(shù),接受一個整數(shù)a,返回另一個函數(shù) // 返回的函數(shù)接受整數(shù)b,返回a + b的結(jié)果 ------------------------------------------------------------------------ 2.部分應(yīng)用: Function<Integer, Integer> add5 = adder.apply(5); // 調(diào)用adder.apply(5)固定了第一個參數(shù)a=5 // 返回的新函數(shù)add5只需要一個參數(shù)b,計算5 + b ------------------------------------------------------------------------ 3.完成計算: add5.apply(3) // 返回8 // 向add5提供第二個參數(shù)3 // 執(zhí)行計算5 + 3 = 8 ------------------------------------------------------------------------ </code>
5.1.4. 實際運用場景
5.1.4.1. 創(chuàng)建帶前綴的日志函數(shù)
// 創(chuàng)建帶前綴的日志函數(shù)
Function<String, Consumer<String>> logger = prefix -> message ->
System.out.println("[" + prefix + "] " + message);
Consumer<String> appLog = logger.apply("APP");
appLog.accept("Starting system..."); // 輸出: [APP] Starting system...5.1.4.2. Web開發(fā)中的中間件:
// 模擬中間件鏈
Function<String, Function<HttpRequest, HttpResponse>> middleware =
authToken -> request -> {
if(validToken(authToken)) {
return process(request);
}
return new HttpResponse(403);
};5.2. 異常處理包裝
5.2.1. 示例
@FunctionalInterface
interface CheckedFunction<T, R> {
R apply(T t) throws Exception;
}
public static <T, R> Function<T, R> wrap(CheckedFunction<T, R> checkedFunction) {
return t -> {
try {
return checkedFunction.apply(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
// 使用
Function<String, Integer> safeParse = wrap(Integer::parseInt);5.2.2. 問題背景
在Java中,函數(shù)式接口如Function不允許拋出受檢異常,但像Integer.parseInt()這樣的方法會拋出NumberFormatException(雖然是運行時異常,但其他方法如IO操作會拋出受檢異常)。
5.2.3. 示例解析
1.@FunctionalInterface確保它只有一個抽象方法
@FunctionalInterface
--------------------------------------------------------------------------------------
2.這是一個自定義的函數(shù)式接口
與標準Function接口的關(guān)鍵區(qū)別是允許apply()拋出Exception
interface CheckedFunction<T, R> {
R apply(T t) throws Exception;
}
--------------------------------------------------------------------------------------
3.將CheckedFunction轉(zhuǎn)換為標準的Function
使用try-catch捕獲所有異常
將受檢異常包裝為RuntimeException
public static <T, R> Function<T, R> wrap(CheckedFunction<T, R> checkedFunction) {
return t -> { // 返回一個標準的Function
try {
return checkedFunction.apply(t); // 執(zhí)行可能拋出異常的操作
} catch (Exception e) {
throw new RuntimeException(e); // 包裝為運行時異常
}
};
}
--------------------------------------------------------------------------------------
4.Integer::parseInt方法引用作為CheckedFunction輸入
返回的safeParse是一個標準的Function,可以在Stream等場景直接使用
Function<String, Integer> safeParse = wrap(Integer::parseInt);5.2.4. 深入理解
5.2.4.1. 為什么需要這樣包裝?
Java的函數(shù)式編程接口(如Function、Consumer等)的抽象方法不允許拋出受檢異常。這種包裝模式解決了:
允許在lambda表達式中使用會拋出異常的方法
保持與現(xiàn)有函數(shù)式接口的兼容性
5.2.4.2. 統(tǒng)一異常處理方式
定制異常處理:
public static <T, R> Function<T, R> wrap(
CheckedFunction<T, R> checkedFunction,
Function<Exception, R> exceptionHandler) {
return t -> {
try {
return checkedFunction.apply(t);
} catch (Exception e) {
return exceptionHandler.apply(e);
}
};
}
// 使用:解析失敗時返回null
Function<String, Integer> safeParse = wrap(
Integer::parseInt,
e -> null
);5.2.5. 實際應(yīng)用場景
5.2.5.1. Stream處理中的異常
List<String> numbers = Arrays.asList("1", "2", "abc", "4");
// 不使用包裝會很難處理異常
List<Integer> parsed = numbers.stream()
.map(wrap(Integer::parseInt)) // 使用包裝方法
.collect(Collectors.toList()); // 遇到錯誤會拋出RuntimeException5.2.5.2. 文件操作
// 讀取文件所有行
Function<Path, List<String>> readLines = wrap(path ->
Files.readAllLines(path, StandardCharsets.UTF_8));
List<String> lines = readLines.apply(Paths.get("data.txt"));5.2.5.3. 數(shù)據(jù)庫操作
// 查詢用戶
Function<Long, User> findUser = wrap(id ->
userRepository.findById(id).orElseThrow());
User user = findUser.apply(123L);5.2.6. 完整工具類示例
public class FunctionWrappers {
@FunctionalInterface
public interface CheckedFunction<T, R> {
R apply(T t) throws Exception;
}
// 基本包裝方法
public static <T, R> Function<T, R> wrap(CheckedFunction<T, R> checkedFunction) {
return t -> {
try {
return checkedFunction.apply(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
// 帶異常處理的包裝
public static <T, R> Function<T, R> wrap(
CheckedFunction<T, R> checkedFunction,
Function<Exception, R> exceptionHandler) {
return t -> {
try {
return checkedFunction.apply(t);
} catch (Exception e) {
return exceptionHandler.apply(e);
}
};
}
// 針對Consumer的包裝
@FunctionalInterface
public interface CheckedConsumer<T> {
void accept(T t) throws Exception;
}
public static <T> Consumer<T> wrapConsumer(CheckedConsumer<T> checkedConsumer) {
return t -> {
try {
checkedConsumer.accept(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
}6. Java Consumer 接口詳解
Consumer 是 Java 8 引入的一個核心函數(shù)式接口,屬于 java.util.function 包。它代表一個"接受單個輸入?yún)?shù)但不返回結(jié)果"的操作。
6.1. Consumer 的基本定義
T:輸入?yún)?shù)類型
核心方法:accept(T t) 執(zhí)行操作
特點:有輸入無輸出(與 Function 不同)
@FunctionalInterface
public interface Consumer<T> {
void accept(T t); // 核心抽象方法
// 默認方法(組合操作)
default Consumer<T> andThen(Consumer<? super T> after)
}6.2. 核心方法解析
1. accept(T t)
執(zhí)行消費操作:
Consumer<String> printConsumer = s -> System.out.println(s);
printConsumer.accept("Hello"); // 輸出 "Hello"
----------------------------------------------------------------------------
2. andThen(Consumer after)
組合多個 Consumer(按順序執(zhí)行):
Consumer<String> printUpper = s -> System.out.println(s.toUpperCase());
Consumer<String> printLower = s -> System.out.println(s.toLowerCase());
Consumer<String> combined = printUpper.andThen(printLower);
combined.accept("Java");
// 輸出:
// JAVA
// java
----------------------------------------------------------------------------6.3. Consumer 的變體
Java 還提供了 Consumer 的特化版本:
接口 方法簽名 說明
IntConsumer void accept(int) 處理 int 類型
LongConsumer void accept(long) 處理 long 類型
DoubleConsumer void accept(double) 處理 double 類型
BiConsumer<T,U> void accept(T t, U u) 處理兩個參數(shù)
6.4. 典型運用場景
6.4.1. 集合遍歷
List<String> names = List.of("Alice", "Bob", "Charlie");
// 傳統(tǒng)方式
for (String name : names) {
System.out.println(name);
}
// 使用 Consumer
names.forEach(name -> System.out.println(name));
// 使用方法引用
names.forEach(System.out::println);6.4.2. 資源處理
Consumer<Path> fileProcessor = path -> {
try {
String content = Files.readString(path);
System.out.println(content);
} catch (IOException e) {
throw new RuntimeException(e);
}
};
fileProcessor.accept(Paths.get("data.txt"));6.4.3. 對象配置
class Person {
String name;
int age;
// setters...
}
Consumer<Person> configurator = p -> {
p.setName("Default");
p.setAge(30);
};
Person person = new Person();
configurator.accept(person);6.4.4. 日志記錄
BiConsumer<String, Object> logger = (level, msg) -> {
System.out.printf("[%s] %s - %s%n",
LocalDateTime.now(), level, msg);
};
logger.accept("INFO", "Application started");6.5. 相關(guān)接口對比
接口 方法簽名 特點
Consumer<T> void accept(T) 有輸入無輸出
Function<T,R> R apply(T) 有輸入有輸出
Supplier<T> T get() 無輸入有輸出
Predicate<T> boolean test(T) 有輸入,輸出布爾值
6.6. 高級用法
6.6.1. 異常處理包裝
@FunctionalInterface
interface CheckedConsumer<T> {
void accept(T t) throws Exception;
}
public static <T> Consumer<T> wrap(CheckedConsumer<T> checkedConsumer) {
return t -> {
try {
checkedConsumer.accept(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
// 使用
Consumer<Path> safeFileReader = wrap(path -> {
String content = Files.readString(path);
System.out.println(content);
});6.6.2. 條件消費
public static <T> Consumer<T> conditional(
Predicate<T> condition,
Consumer<T> consumer) {
return t -> {
if (condition.test(t)) {
consumer.accept(t);
}
};
}
// 只打印長度大于3的字符串
Consumer<String> selectivePrint = conditional(
s -> s.length() > 3,
System.out::println
);
selectivePrint.accept("Hi"); // 不輸出
selectivePrint.accept("Hello"); // 輸出 "Hello"6.6.3. 構(gòu)建處理管道
Consumer<String> pipeline = ((Consumer<String>) s -> System.out.println("Original: " + s))
.andThen(s -> System.out.println("Upper: " + s.toUpperCase()))
.andThen(s -> System.out.println("Length: " + s.length()));
pipeline.accept("Java");
/* 輸出:
Original: Java
Upper: JAVA
Length: 4
*/6.7. 注意事項
6.7.1. 命名規(guī)范
// 好的命名 Consumer<String> logMessage = msg -> System.out.println(msg); // 不好的命名 Consumer<String> c = m -> System.out.println(m);
6.7.2. 保持簡潔
// 簡單邏輯直接用lambda names.forEach(System.out::println); // 復(fù)雜邏輯提取方法 names.forEach(this::processName);
6.7.3. 避免副作用
// 不推薦(修改外部狀態(tài))
List<String> result = new ArrayList<>();
names.forEach(name -> result.add(name.toUpperCase()));
// 推薦(使用stream)
List<String> result = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());到此這篇關(guān)于Java 中的 Function 接口的文章就介紹到這了,更多相關(guān)Java Function 接口內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java8中Function接口的使用方法詳解
- Java的函數(shù)式接口@FunctionalInterface的使用說明
- JAVA中的函數(shù)式接口Function和BiFunction詳解
- Java 8函數(shù)式接口Function BiFunction DoubleFunction區(qū)別
- Java?8?中?Function?接口使用方法介紹
- 妙用Java8中的Function接口消滅if...else
- Java8函數(shù)式接口java.util.function速查大全
- JAVA8之函數(shù)式編程Function接口用法
- Java 8 Function函數(shù)式接口及函數(shù)式接口實例
相關(guān)文章
Maven工程引入依賴失敗Dependencies全部飄紅問題
這篇文章主要介紹了Maven工程引入依賴失敗Dependencies全部飄紅問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08
前端如何調(diào)用后端接口進行數(shù)據(jù)交互詳解(axios和SpringBoot)
一般來講前端不會給后端接口,而是后端給前端接口的情況比較普遍,下面這篇文章主要給大家介紹了關(guān)于前端如何調(diào)用后端接口進行數(shù)據(jù)交互的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-03-03

