Function 是 Java 8 引入的一个核心函数式接口,属于 java.util.function 包。它代表一个"接受一个参数并产生结果"的函数。
T:输入类型(参数类型)
R:结果类型(返回类型)
唯一抽象方法:R apply(T t)
@FunctionalInterface
public interface Function {
R apply(T t);
// 还包含两个默认方法(后面会讲解)
default Function compose(Function super V, ? extends T> before)
default Function andThen(Function super R, ? extends V> after)
}
// 1. 定义一个将字符串转为长度的函数
Function lengthFunction = s -> s.length();
System.out.println(lengthFunction.apply("Hello")); // 输出 5
// 2. 定义将数字转为字符串的函数
Function stringify = num -> "Number: " + num;
System.out.println(stringify.apply(42)); // 输出 "Number: 42"
// 1. apply(T t)
// 核心方法,执行函数转换:
Function upperCase = s -> s.toUpperCase();
String result = upperCase.apply("hello"); // 返回 "HELLO"
-------------------------------------------------------------------------
// 2. compose(Function before)
// 组合函数,先执行 before 函数,再执行当前函数:
Function intToStr = i -> "Value: " + i;
Function strToInt = s -> Integer.parseInt(s);
// 先执行 strToInt,再执行 intToStr
Function composed = intToStr.compose(strToInt);
System.out.println(composed.apply("123")); // 输出 "Value: 123"
-------------------------------------------------------------------------
// 3. andThen(Function after)
// 组合函数,先执行当前函数,再执行 after 函数:
Function parse = Integer::parseInt;
Function square = i -> i * i;
// 先执行 parse,再执行 square
Function parseThenSquare = parse.andThen(square);
System.out.println(parseThenSquare.apply("5")); // 输出 25
-------------------------------------------------------------------------
// 4. identity()
// 静态方法,返回一个总是返回其输入参数的函数:
Function identity = Function.identity();
System.out.println(identity.apply("Same")); // 输出 "Same"
-------------------------------------------------------------------------
Java 还提供了 Function 的一些特化版本:
接口 方法签名 等价于
IntFunction
DoubleFunction
LongFunction
IntFunction intToString = i -> "Num: " + i;
System.out.println(intToString.apply(10)); // 输出 "Num: 10"
接口 方法签名
BiFunction
BiFunction concat = (s1, s2) -> s1 + s2;
System.out.println(concat.apply("Hello", "World")); // 输出 HelloWorld
List names = Arrays.asList("Alice", "Bob", "Charlie");
// 将名字转为大写
List upperNames = names.stream()
.map(name -> name.toUpperCase()) // 这里使用Function
.collect(Collectors.toList());
// 定义不同的处理策略
Map> 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"
Function times2 = x -> x * 2;
Function minus1 = x -> x - 1;
// 组合函数:先乘2再减1
Function chain = times2.andThen(minus1);
System.out.println(chain.apply(10)); // 输出 19
对象创建开销:每次使用 lambda 表达式都会创建一个新对象
自动装箱问题:对于基本类型,使用特化接口(如 IntFunction)更高效
JIT 优化:HotSpot 虚拟机会对 lambda 进行优化
// 将二元函数转换为一元函数链
Function> adder = a -> b -> a + b;
Function add5 = adder.apply(5);
System.out.println(add5.apply(3)); // 输出 8
柯里化的核心思想是:将一个接受多个参数的函数,转换成一系列使用一个参数的函数。
原始加法函数
BiFunction normalAdd = (a, b) -> a + b;
柯里化后的加法函数
BiFunction normalAdd = (a, b) -> a + b;
Function> adder = a -> b -> a + b;
Function add5 = adder.apply(5);
System.out.println(add5.apply(3)); // 输出 8
------------------------------------------------------------------------
1.定义柯里化函数:
Function> adder = a -> b -> a + b;
// 这是一个函数,接受一个整数a,返回另一个函数
// 返回的函数接受整数b,返回a + b的结果
------------------------------------------------------------------------
2.部分应用:
Function add5 = adder.apply(5);
// 调用adder.apply(5)固定了第一个参数a=5
// 返回的新函数add5只需要一个参数b,计算5 + b
------------------------------------------------------------------------
3.完成计算:
add5.apply(3) // 返回8
// 向add5提供第二个参数3
// 执行计算5 + 3 = 8
------------------------------------------------------------------------
// 创建带前缀的日志函数
Function> logger = prefix -> message ->
System.out.println("[" + prefix + "] " + message);
Consumer appLog = logger.apply("APP");
appLog.accept("Starting system..."); // 输出: [APP] Starting system...
// 模拟中间件链
Function> middleware =
authToken -> request -> {
if(validToken(authToken)) {
return process(request);
}
return new HttpResponse(403);
};
@FunctionalInterface
interface CheckedFunction {
R apply(T t) throws Exception;
}
public static Function wrap(CheckedFunction checkedFunction) {
return t -> {
try {
return checkedFunction.apply(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
// 使用
Function safeParse = wrap(Integer::parseInt);
在Java中,函数式接口如Function不允许抛出受检异常,但像Integer.parseInt()这样的方法会抛出NumberFormatException(虽然是运行时异常,但其他方法如IO操作会抛出受检异常)。
1.@FunctionalInterface确保它只有一个抽象方法
@FunctionalInterface
--------------------------------------------------------------------------------------
2.这是一个自定义的函数式接口
与标准Function接口的关键区别是允许apply()抛出Exception
interface CheckedFunction {
R apply(T t) throws Exception;
}
--------------------------------------------------------------------------------------
3.将CheckedFunction转换为标准的Function
使用try-catch捕获所有异常
将受检异常包装为RuntimeException
public static Function wrap(CheckedFunction checkedFunction) {
return t -> { // 返回一个标准的Function
try {
return checkedFunction.apply(t); // 执行可能抛出异常的操作
} catch (Exception e) {
throw new RuntimeException(e); // 包装为运行时异常
}
};
}
--------------------------------------------------------------------------------------
4.Integer::parseInt方法引用作为CheckedFunction输入
返回的safeParse是一个标准的Function,可以在Stream等场景直接使用
Function safeParse = wrap(Integer::parseInt);
Java的函数式编程接口(如Function、Consumer等)的抽象方法不允许抛出受检异常。这种包装模式解决了:
允许在lambda表达式中使用会抛出异常的方法
保持与现有函数式接口的兼容性
定制异常处理:
public static Function wrap(
CheckedFunction checkedFunction,
Function exceptionHandler) {
return t -> {
try {
return checkedFunction.apply(t);
} catch (Exception e) {
return exceptionHandler.apply(e);
}
};
}
// 使用:解析失败时返回null
Function safeParse = wrap(
Integer::parseInt,
e -> null
);
List numbers = Arrays.asList("1", "2", "abc", "4");
// 不使用包装会很难处理异常
List parsed = numbers.stream()
.map(wrap(Integer::parseInt)) // 使用包装方法
.collect(Collectors.toList()); // 遇到错误会抛出RuntimeException
// 读取文件所有行
Function> readLines = wrap(path ->
Files.readAllLines(path, StandardCharsets.UTF_8));
List lines = readLines.apply(Paths.get("data.txt"));
// 查询用户
Function findUser = wrap(id ->
userRepository.findById(id).orElseThrow());
User user = findUser.apply(123L);
public class FunctionWrappers {
@FunctionalInterface
public interface CheckedFunction {
R apply(T t) throws Exception;
}
// 基本包装方法
public static Function wrap(CheckedFunction checkedFunction) {
return t -> {
try {
return checkedFunction.apply(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
// 带异常处理的包装
public static Function wrap(
CheckedFunction checkedFunction,
Function exceptionHandler) {
return t -> {
try {
return checkedFunction.apply(t);
} catch (Exception e) {
return exceptionHandler.apply(e);
}
};
}
// 针对Consumer的包装
@FunctionalInterface
public interface CheckedConsumer {
void accept(T t) throws Exception;
}
public static Consumer wrapConsumer(CheckedConsumer checkedConsumer) {
return t -> {
try {
checkedConsumer.accept(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
}
Consumer 是 Java 8 引入的一个核心函数式接口,属于 java.util.function 包。它代表一个"接受单个输入参数但不返回结果"的操作。
T:输入参数类型
核心方法:accept(T t) 执行操作
特点:有输入无输出(与 Function 不同)
@FunctionalInterface
public interface Consumer {
void accept(T t); // 核心抽象方法
// 默认方法(组合操作)
default Consumer andThen(Consumer super T> after)
}
1. accept(T t)
执行消费操作:
Consumer printConsumer = s -> System.out.println(s);
printConsumer.accept("Hello"); // 输出 "Hello"
----------------------------------------------------------------------------
2. andThen(Consumer after)
组合多个 Consumer(按顺序执行):
Consumer printUpper = s -> System.out.println(s.toUpperCase());
Consumer printLower = s -> System.out.println(s.toLowerCase());
Consumer combined = printUpper.andThen(printLower);
combined.accept("Java");
// 输出:
// JAVA
// java
----------------------------------------------------------------------------
Java 还提供了 Consumer 的特化版本:
接口 方法签名 说明
IntConsumer void accept(int) 处理 int 类型
LongConsumer void accept(long) 处理 long 类型
DoubleConsumer void accept(double) 处理 double 类型
BiConsumer
List names = List.of("Alice", "Bob", "Charlie");
// 传统方式
for (String name : names) {
System.out.println(name);
}
// 使用 Consumer
names.forEach(name -> System.out.println(name));
// 使用方法引用
names.forEach(System.out::println);
Consumer 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"));
class Person {
String name;
int age;
// setters...
}
Consumer configurator = p -> {
p.setName("Default");
p.setAge(30);
};
Person person = new Person();
configurator.accept(person);
BiConsumer logger = (level, msg) -> {
System.out.printf("[%s] %s - %s%n",
LocalDateTime.now(), level, msg);
};
logger.accept("INFO", "Application started");
接口 方法签名 特点
Consumer
Function
Supplier
Predicate
@FunctionalInterface
interface CheckedConsumer {
void accept(T t) throws Exception;
}
public static Consumer wrap(CheckedConsumer checkedConsumer) {
return t -> {
try {
checkedConsumer.accept(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
// 使用
Consumer safeFileReader = wrap(path -> {
String content = Files.readString(path);
System.out.println(content);
});
public static Consumer conditional(
Predicate condition,
Consumer consumer) {
return t -> {
if (condition.test(t)) {
consumer.accept(t);
}
};
}
// 只打印长度大于3的字符串
Consumer selectivePrint = conditional(
s -> s.length() > 3,
System.out::println
);
selectivePrint.accept("Hi"); // 不输出
selectivePrint.accept("Hello"); // 输出 "Hello"
Consumer pipeline = ((Consumer) 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
*/
// 好的命名
Consumer logMessage = msg -> System.out.println(msg);
// 不好的命名
Consumer c = m -> System.out.println(m);
// 简单逻辑直接用lambda
names.forEach(System.out::println);
// 复杂逻辑提取方法
names.forEach(this::processName);
// 不推荐(修改外部状态)
List result = new ArrayList<>();
names.forEach(name -> result.add(name.toUpperCase()));
// 推荐(使用stream)
List result = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());