java Fuction中的10个实用高级用法

在 Java 中,Function<T, R> 是函数式接口的核心代表,用于表明 “输入一个类型 T 的参数,返回一个类型 R 的结果” 的函数。除了基础的 apply() 方法,结合 Java 8+ 的 Stream、Optional、方法引用、复合函数等特性,Function 还有许多高级玩法,能极大简化代码、提升可读性。以下是 10 个实用的高级用法,附代码示例:

java Fuction中的10个实用高级用法

一、复合函数:andThen()与compose()链式调用

Function 提供了两个默认方法用于函数组合,实现 “链式执行”:

  • andThen(Function<? super R, ? extends V> after):先执行当前函数,再将结果作为参数传给 after 函数(先当前后 after)。
  • compose(Function<? super V, ? extends T> before):先执行 before 函数,再将结果作为参数传给当前函数(先 before 后当前)。

示例:数据转换链式处理

import java.util.function.Function;

public class FunctionDemo {
    public static void main(String[] args) {
        // 1. 字符串转整数
        Function<String, Integer> strToInt = Integer::parseInt;
        // 2. 整数翻倍
        Function<Integer, Integer> doubleNum = n -> n * 2;
        // 3. 整数转字符串(加前缀)
        Function<Integer, String> intToStrWithPrefix = n -> "结果:" + n;

        // 组合1:strToInt → doubleNum → intToStrWithPrefix(andThen链式)
        Function<String, String> process1 = strToInt.andThen(doubleNum).andThen(intToStrWithPrefix);
        System.out.println(process1.apply("10")); // 输出:结果:20

        // 组合2:先执行doubleNum(需适配参数),再执行strToInt(compose)
        // 注意:compose的参数函数输入类型需匹配最终输入,输出类型需匹配当前函数输入
        Function<String, Integer> process2 = strToInt.compose(s -> Integer.parseInt(s) + 5); // 先s→int+5,再转int(此处仅演示逻辑)
        System.out.println(process2.apply("10")); // 输出:15(先10+5=15,再转int)
    }
}

二、与Stream结合:自定义映射逻辑

Stream.map(Function<? super T, ? extends R>) 是 Function 最常用的场景之一,但高级用法在于复杂映射逻辑的复用(将重复的 map 逻辑抽取为独立 Function)。

示例:抽取通用映射函数

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

class User {
    private String name;
    private int age;
    // 构造器、getter省略
    public User(String name, int age) { this.name = name; this.age = age; }
    public String getName() { return name; }
    public int getAge() { return age; }
}

public class StreamFunctionDemo {
    // 抽取:User → 用户名(复用逻辑)
    private static final Function<User, String> USER_TO_NAME = User::getName;
    // 抽取:User → 年龄+10
    private static final Function<User, Integer> USER_AGE_PLUS_10 = user -> user.getAge() + 10;

    public static void main(String[] args) {
        List<User> users = Arrays.asList(
            new User("Alice", 20),
            new User("Bob", 25)
        );

        // 复用Function进行map
        List<String> names = users.stream().map(USER_TO_NAME).collect(Collectors.toList());
        List<Integer> agesPlus10 = users.stream().map(USER_AGE_PLUS_10).collect(Collectors.toList());

        System.out.println(names); // [Alice, Bob]
        System.out.println(agesPlus10); // [30, 35]
    }
}

三、与Optional结合:安全的链式转换

Optional.map(Function<? super T, ? extends U>) 可避免空指针,结合 Function 实现 “多步安全转换”(若中间步骤为 null,直接返回 Optional.empty())。

示例:多层对象安全取值

import java.util.Optional;
import java.util.function.Function;

class Address {
    private String city;
    public Address(String city) { this.city = city; }
    public String getCity() { return city; }
}

class User {
    private Address address;
    public User(Address address) { this.address = address; }
    public Address getAddress() { return address; }
}

public class OptionalFunctionDemo {
    public static void main(String[] args) {
        // 函数1:User → Address
        Function<User, Address> userToAddress = User::getAddress;
        // 函数2:Address → 城市(大写)
        Function<Address, String> addressToCity = addr -> addr.getCity().toUpperCase();

        // 安全链式转换:User → Address → 城市(避免NPE)
        User userWithAddress = new User(new Address("Shanghai"));
        User userWithoutAddress = new User(null);

        String city1 = Optional.ofNullable(userWithAddress)
                               .map(userToAddress) // 第一步转换
                               .map(addressToCity) // 第二步转换
                               .orElse("未知城市");
        System.out.println(city1); // SHANGHAI

        String city2 = Optional.ofNullable(userWithoutAddress)
                               .map(userToAddress)
                               .map(addressToCity)
                               .orElse("未知城市");
        System.out.println(city2); // 未知城市
    }
}

四、方法引用简化Function定义

对于 “直接调用某个方法” 的简单逻辑,无需用 lambda 表达式,直接用方法引用(::)简化 Function 定义,代码更简洁。

示例:方法引用的 3 种场景

import java.util.function.Function;

public class MethodReferenceDemo {
    public static void main(String[] args) {
        // 1. 静态方法引用:String → Integer(Integer.parseInt(String))
        Function<String, Integer> strToInt = Integer::parseInt;

        // 2. 实例方法引用(对象实例):String → Integer("abc".length())
        String prefix = "前缀_";
        Function<String, String> addPrefix = prefix::concat; // 等价于 s -> prefix.concat(s)

        // 3. 实例方法引用(类名):String → Character("abc".charAt(0))
        Function<String, Character> firstChar = s -> s.charAt(0);
        // 更简洁:类名::方法名(适用于方法参数为Function输入的场景)
        Function<String, Integer> strLength = String::length;

        System.out.println(strToInt.apply("20")); // 20
        System.out.println(addPrefix.apply("test")); // 前缀_test
        System.out.println(strLength.apply("hello")); // 5
    }
}

五、Function作为方法参数:通用化逻辑

将 Function 作为方法参数,让方法支持 “自定义逻辑注入”,实现方法的通用性(类似 “策略模式” 的简化版)。

示例:通用数据处理器

import java.util.function.Function;

public class FunctionAsParameterDemo {
    // 通用方法:接收数据和处理函数,返回处理结果
    public static <T, R> R processData(T data, Function<T, R> processor) {
        System.out.println("处理数据:" + data);
        return processor.apply(data);
    }

    public static void main(String[] args) {
        // 场景1:字符串转大写
        String upperStr = processData("hello", String::toUpperCase);
        System.out.println(upperStr); // HELLO

        // 场景2:整数平方
        Integer square = processData(10, n -> n * n);
        System.out.println(square); // 100

        // 场景3:字符串长度翻倍
        Integer doubleLength = processData("java", s -> s.length() * 2);
        System.out.println(doubleLength); // 8
    }
}

六、BiFunction处理双参数场景

Function 仅支持单输入,若需处理两个输入参数,可使用 BiFunction<T, U, R>(输入 T 和 U,返回 R),配合 andThen() 可扩展为多步处理。

示例:双参数计算 + 链式转换

import java.util.function.BiFunction;
import java.util.function.Function;

public class BiFunctionDemo {
    public static void main(String[] args) {
        // 双参数函数:两个整数相加
        BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
        // 单参数函数:整数转字符串(加描述)
        Function<Integer, String> intToDesc = n -> "总和:" + n;

        // 组合:先相加,再转描述
        String result = add.andThen(intToDesc).apply(10, 20);
        System.out.println(result); // 总和:30

        // 复杂场景:拼接两个字符串并转为大写
        BiFunction<String, String, String> concat = (s1, s2) -> s1 + "-" + s2;
        Function<String, String> toUpper = String::toUpperCase;
        String concatResult = concat.andThen(toUpper).apply("hello", "world");
        System.out.println(concatResult); // HELLO-WORLD
    }
}

七、UnaryOperator简化 “输入输出类型一样” 场景

若 Function 的输入类型 T 和输出类型 R 一样(如 “整数翻倍”“字符串截取”),可使用 UnaryOperator<T>(Function<T, T> 的子类),无需重复声明类型,代码更简洁。

示例:UnaryOperator 替代同类型 Function

import java.util.function.UnaryOperator;

public class UnaryOperatorDemo {
    public static void main(String[] args) {
        // 等价于 Function<Integer, Integer> doubleNum = n -> n * 2;
        UnaryOperator<Integer> doubleNum = n -> n * 2;
        // 等价于 Function<String, String> truncate = s -> s.substring(0, 3);
        UnaryOperator<String> truncate = s -> s.substring(0, 3);

        System.out.println(doubleNum.apply(5)); // 10
        System.out.println(truncate.apply("Java8")); // Jav

        // 组合使用(andThen同样适用)
        UnaryOperator<Integer> add5 = n -> n + 5;
        UnaryOperator<Integer> process = add5.andThen(doubleNum); // 先+5,再翻倍
        System.out.println(process.apply(10)); // 30
    }
}

八、Function与缓存结合:缓存计算结果

对于耗时的 Function 计算(如数据库查询、复杂运算),可包装 Function 实现结果缓存(避免重复计算),典型场景:数据字典翻译、ID→名称映射。

示例:带缓存的 Function 包装器

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

public class CachedFunctionDemo {
    // 包装Function,添加缓存逻辑
    public static <T, R> Function<T, R> cached(Function<T, R> function) {
        Map<T, R> cache = new HashMap<>();
        return t -> cache.computeIfAbsent(t, function); // 不存在则计算并缓存
    }

    public static void main(String[] args) {
        // 模拟耗时计算:ID→用户名(假设查询数据库)
        Function<Long, String> idToName = id -> {
            System.out.println("查询数据库:ID=" + id); // 仅第一次执行
            return id == 1L ? "Alice" : "Bob";
        };

        // 包装为带缓存的Function
        Function<Long, String> cachedIdToName = cached(idToName);

        // 第一次调用:查询数据库并缓存
        System.out.println(cachedIdToName.apply(1L)); // 查询数据库:ID=1 → Alice
        // 第二次调用:直接取缓存
        System.out.println(cachedIdToName.apply(1L)); // Alice(无数据库查询)
        System.out.println(cachedIdToName.apply(2L)); // 查询数据库:ID=2 → Bob
    }
}

java Fuction中的10个实用高级用法

九、Function实现类型转换工具类

利用 Function 的通用性,可封装通用类型转换工具,统一处理不同对象间的转换(替代繁琐的 setter 赋值)。

示例:对象转换工具类

import java.util.function.Function;

// DTO类
class UserDTO {
    private String username;
    private int userAge;
    // 构造器、getter省略
    public UserDTO(String username, int userAge) { this.username = username; this.userAge = userAge; }
    public String getUsername() { return username; }
    public int getUserAge() { return userAge; }
}

// 实体类
class UserEntity {
    private String name;
    private int age;
    // 构造器、getter/setter省略
    public UserEntity(String name, int age) { this.name = name; this.age = age; }
    public String getName() { return name; }
    public int getAge() { return age; }
}

public class ConverterDemo {
    // 通用转换方法:接收源对象和转换函数,返回目标对象
    public static <S, T> T convert(S source, Function<S, T> converter) {
        return converter.apply(source);
    }

    public static void main(String[] args) {
        // 定义DTO→Entity的转换规则
        Function<UserDTO, UserEntity> dtoToEntity = dto -> 
            new UserEntity(dto.getUsername(), dto.getUserAge());

        // 定义Entity→DTO的转换规则
        Function<UserEntity, UserDTO> entityToDto = entity -> 
            new UserDTO(entity.getName(), entity.getAge());

        // 转换示例
        UserDTO dto = new UserDTO("Charlie", 30);
        UserEntity entity = convert(dto, dtoToEntity);
        System.out.println(entity.getName() + ":" + entity.getAge()); // Charlie:30

        UserDTO dto2 = convert(entity, entityToDto);
        System.out.println(dto2.getUsername() + ":" + dto2.getUserAge()); // Charlie:30
    }
}

十、Function与枚举结合:实现策略枚举

枚举 +Function 可实现 “策略枚举”,将不同策略(逻辑)封装在枚举常量中,统一管理,避免大量 if-else。

示例:支付方式策略枚举

import java.util.function.Function;

// 支付金额计算策略枚举
enum PaymentStrategy {
    // 枚举常量:策略名称 + 计算函数(Function<订单金额, 实际支付金额>)
    NORMAL(amount -> amount), // 原价
    DISCOUNT_90(amount -> amount * 0.9), // 9折
    FULL_100_MINUS_20(amount -> amount >= 100 ? amount - 20 : amount); // 满100减20

    // 持有Function作为策略逻辑
    private final Function<Double, Double> calculator;

    PaymentStrategy(Function<Double, Double> calculator) {
        this.calculator = calculator;
    }

    // 执行计算
    public double calculate(double amount) {
        return calculator.apply(amount);
    }
}

public class StrategyEnumDemo {
    public static void main(String[] args) {
        double orderAmount = 150.0;

        // 不同策略计算
        double normalPay = PaymentStrategy.NORMAL.calculate(orderAmount);
        double discountPay = PaymentStrategy.DISCOUNT_90.calculate(orderAmount);
        double fullReducePay = PaymentStrategy.FULL_100_MINUS_20.calculate(orderAmount);

        System.out.println("原价支付:" + normalPay); // 150.0
        System.out.println("9折支付:" + discountPay); // 135.0
        System.out.println("满减支付:" + fullReducePay); // 130.0
    }
}

核心总结

Function 及其相关接口(BiFunction、UnaryOperator 等)的核心价值是将 “逻辑” 抽象为可传递、可组合的对象,配合 Java 8+ 的流式 API、Optional 等特性,可实现:

  1. 简化重复逻辑(抽取为独立 Function);
  2. 避免空指针(与 Optional 结合);
  3. 减少 if-else(策略枚举);
  4. 提升代码通用性(作为方法参数)。

这些玩法覆盖了日常开发中的大部分场景,合理使用能让代码更简洁、更易维护。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容