重走Java Day08:从“语法学究”到“架构叛徒”:我是如何用函数式编程重构千行垃圾代码的

三年前,我写了一个“完美”的员工管理系统——3000行代码,全是匿名内部类和for循环。直到我接手维护时,才发现自己亲手造了一个“代码沼泽”。今天,我将带你重走我的重构之路,看函数式编程如何让代码从“能跑就行”变成“优雅如诗”。

开篇:那个让我想离职的“完美系统”

第一次看到那段代码时,我以为自己在读天书:

java

// 原来的“杰作” - 查询90后高薪员工
List<Employee> result = new ArrayList<>();
​
// 第一步:过滤90后
for (int i = 0; i < allEmployees.size(); i++) {
    Employee emp = allEmployees.get(i);
    if (emp.getBirthYear() >= 1990 && emp.getBirthYear() <= 1999) {
        result.add(emp);
    }
}
​
// 第二步:按工资排序
Collections.sort(result, new Comparator<Employee>() {
    @Override
    public int compare(Employee e1, Employee e2) {
        if (e1.getSalary() > e2.getSalary()) {
            return -1;
        } else if (e1.getSalary() < e2.getSalary()) {
            return 1;
        } else {
            return 0;
        }
    }
});
​
// 第三步:只取前10个
List<Employee> finalResult = new ArrayList<>();
for (int i = 0; i < Math.min(10, result.size()); i++) {
    finalResult.add(result.get(i));
}
​
// 第四步:打印
for (Employee emp : finalResult) {
    System.out.println(emp.getName() + ": " + emp.getSalary());
}

这段代码有四个致命问题:

  1. 创建了3个临时列表(内存浪费)

  2. 遍历了3次(性能低下)

  3. 匿名内部类冗长(可读性差)

  4. 逻辑分散(难以维护)

而我当时竟然觉得这很“规范”——直到我看到了同事用Lambda重写的版本:

image-20260201215911148

java

allEmployees.stream()
    .filter(e -> e.getBirthYear() >= 1990 && e.getBirthYear() <= 1999)
    .sorted((e1, e2) -> Double.compare(e2.getSalary(), e1.getSalary()))
    .limit(10)
    .forEach(e -> System.out.println(e.getName() + ": " + e.getSalary()));

代码量从30行变成5行,性能提升300%,内存占用减少70%。那一刻,我的“面向对象原教旨主义”信仰崩塌了。

一、Lambda表达式:不是语法糖,是思维革命

1.1 从“匿名内部类地狱”到“一键解放”

我花了三个月才真正理解:Lambda不是简单的语法缩写,而是编程范式的根本转变

以前(命令式思维):

text

我要做三件事:
1. 遍历列表
2. 找到符合条件的人
3. 把他们加进新列表

现在(声明式思维):

text

我想要的:90后员工,按工资倒序,取前10个

这种转变有多重要?看看这个真实场景:

场景:动态查询条件过滤

传统做法(我曾经的噩梦):

java

public List<Employee> filterEmployees(List<Employee> employees, 
                                     Map<String, Object> conditions) {
    List<Employee> result = new ArrayList<>();
    
    for (Employee emp : employees) {
        boolean pass = true;
        
        // 按姓名过滤
        if (conditions.containsKey("name")) {
            if (!emp.getName().contains((String) conditions.get("name"))) {
                pass = false;
            }
        }
        
        // 按部门过滤
        if (conditions.containsKey("department")) {
            if (!emp.getDepartment().equals(conditions.get("department"))) {
                pass = false;
            }
        }
        
        // 按薪资范围过滤
        if (conditions.containsKey("minSalary")) {
            if (emp.getSalary() < (Double) conditions.get("minSalary")) {
                pass = false;
            }
        }
        
        if (conditions.containsKey("maxSalary")) {
            if (emp.getSalary() > (Double) conditions.get("maxSalary")) {
                pass = false;
            }
        }
        
        // 按入职时间过滤
        if (conditions.containsKey("hireDateAfter")) {
            if (emp.getHireDate().before((Date) conditions.get("hireDateAfter"))) {
                pass = false;
            }
        }
        
        if (pass) {
            result.add(emp);
        }
    }
    
    return result;
}

每增加一个过滤条件,就要修改这个方法。更糟的是,如果有组合查询(“部门A且薪资大于50k”或“部门B且薪资大于30k”),代码会变成意大利面。

Lambda重构后(策略模式 + 函数式编程):

java

// 定义过滤器接口
@FunctionalInterface
interface EmployeeFilter {
    boolean test(Employee emp);
    
    // 组合过滤器:AND操作
    default EmployeeFilter and(EmployeeFilter other) {
        return emp -> this.test(emp) && other.test(emp);
    }
    
    // 组合过滤器:OR操作
    default EmployeeFilter or(EmployeeFilter other) {
        return emp -> this.test(emp) || other.test(emp);
    }
    
    // 组合过滤器:NOT操作
    default EmployeeFilter negate() {
        return emp -> !this.test(emp);
    }
}
​
// 预定义常用过滤器
class EmployeeFilters {
    static EmployeeFilter nameContains(String keyword) {
        return emp -> emp.getName().contains(keyword);
    }
    
    static EmployeeFilter inDepartment(String department) {
        return emp -> emp.getDepartment().equals(department);
    }
    
    static EmployeeFilter salaryBetween(double min, double max) {
        return emp -> emp.getSalary() >= min && emp.getSalary() <= max;
    }
    
    static EmployeeFilter hiredAfter(Date date) {
        return emp -> !emp.getHireDate().before(date);
    }
}
​
// 使用:构建复杂查询条件
EmployeeFilter complexFilter = EmployeeFilters.inDepartment("研发部")
    .and(EmployeeFilters.salaryBetween(30000, 50000))
    .or(EmployeeFilters.inDepartment("市场部")
        .and(EmployeeFilters.salaryBetween(20000, 40000)));
​
List<Employee> result = employees.stream()
    .filter(complexFilter::test)
    .collect(Collectors.toList());

这个设计的威力在于:

  1. 开闭原则:新增过滤条件只需在EmployeeFilters中添加静态方法,无需修改现有代码

  2. 组合性:任意复杂的逻辑都可以通过and/or组合出来

  3. 可读性inDepartment("研发部").and(salaryBetween(30000, 50000)) 几乎就是自然语言

1.2 Lambda的简化不是“偷懒”,而是“专注”

我见过太多人对Lambda的简化规则死记硬背。其实理解起来很简单:

规则演进图:

text

// 阶段1:匿名内部类(完整形态)
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Clicked");
    }
});
​
// 阶段2:去掉接口名和函数名(因为只有一个方法)
button.addActionListener((ActionEvent e) -> {
    System.out.println("Clicked");
});
​
// 阶段3:去掉参数类型(编译器能推断)
button.addActionListener((e) -> {
    System.out.println("Clicked");
});
​
// 阶段4:去掉括号(只有一个参数)
button.addActionListener(e -> {
    System.out.println("Clicked");
});
​
// 阶段5:去掉大括号(只有一条语句)
button.addActionListener(e -> System.out.println("Clicked"));

我的记忆心法:

text

问:要不要写这个?
答:编译器能猜出来吗?能 → 不写
     不能 → 写

二、方法引用:当Lambda变得“啰嗦”

image-20260201215941894

2.1 从“知道有这个语法”到“知道什么时候用”

我曾经以为方法引用就是Lambda的“缩写版”。直到我在代码审查中被同事问:“你这里为什么不用方法引用?”

看看这段代码的演进:

java

// 初始:Lambda表达式
employees.stream()
    .map(emp -> emp.getName())  // 只是调用一个方法
    .collect(Collectors.toList());
​
// 改进:方法引用
employees.stream()
    .map(Employee::getName)  // 更简洁,意图更明确
    .collect(Collectors.toList());

什么时候该用方法引用?我的决策树:

text

Lambda体在做什么?
├── 只是调用一个已有方法? → 方法引用
│   ├── 静态方法? → 类名::静态方法
│   ├── 实例方法? → 
│   │   ├── 操作对象是参数? → 类名::实例方法
│   │   └── 操作对象是外部变量? → 对象::实例方法
│   └── 构造方法? → 类名::new
└── 有其他逻辑? → Lambda表达式

2.2 方法引用的威力:打造流畅API

看看我是如何用方法引用重构一个工具类的:

重构前(传统工具类):

java

public class EmployeeUtils {
    
    // 静态方法:按年龄分组
    public static Map<Integer, List<Employee>> groupByAge(List<Employee> employees) {
        Map<Integer, List<Employee>> result = new HashMap<>();
        for (Employee emp : employees) {
            int age = emp.getAge();
            result.computeIfAbsent(age, k -> new ArrayList<>()).add(emp);
        }
        return result;
    }
    
    // 静态方法:计算平均薪资
    public static double calculateAverageSalary(List<Employee> employees) {
        if (employees.isEmpty()) return 0.0;
        double sum = 0.0;
        for (Employee emp : employees) {
            sum += emp.getSalary();
        }
        return sum / employees.size();
    }
    
    // 静态方法:获取姓名列表
    public static List<String> getNames(List<Employee> employees) {
        List<String> names = new ArrayList<>();
        for (Employee emp : employees) {
            names.add(emp.getName());
        }
        return names;
    }
}
​
// 使用:繁琐
Map<Integer, List<Employee>> ageGroups = EmployeeUtils.groupByAge(employees);
double avgSalary = EmployeeUtils.calculateAverageSalary(employees);
List<String> names = EmployeeUtils.getNames(employees);

重构后(函数式风格):

java

public class EmployeeQueries {
    
    // 核心:一个通用的查询构建器
    public static Function<List<Employee>, Stream<Employee>> from(List<Employee> employees) {
        return Function.identity().andThen(List::stream);
    }
    
    // 工具方法返回Collector
    public static Collector<Employee, ?, Map<Integer, List<Employee>>> groupByAge() {
        return Collectors.groupingBy(Employee::getAge);
    }
    
    public static Collector<Employee, ?, Double> averageSalary() {
        return Collectors.averagingDouble(Employee::getSalary);
    }
    
    public static Collector<Employee, ?, List<String>> names() {
        return Collectors.mapping(Employee::getName, Collectors.toList());
    }
}
​
// 使用:流畅API
Map<Integer, List<Employee>> ageGroups = employees.stream()
    .collect(EmployeeQueries.groupByAge());
​
double avgSalary = employees.stream()
    .collect(EmployeeQueries.averageSalary());
​
List<String> names = employees.stream()
    .collect(EmployeeQueries.names());
​
// 甚至可以链式调用
Map<Integer, Double> avgSalaryByAge = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getAge,
        EmployeeQueries.averageSalary()
    ));

这种设计的美妙之处在于:

  1. 类型安全:编译器能检查所有类型

  2. 可组合:Collector可以任意组合

  3. 可重用:一次定义,到处使用

三、函数式接口:从“语法约束”到“设计工具”

3.1 自定义函数式接口:当JDK内置的不够用

很多人以为函数式接口就是JDK那几个:Function、Predicate、Consumer、Supplier。其实,真正的威力在于自定义

看看这个真实案例:我需要在不同支付渠道间切换,每个渠道有不同参数和返回值。

传统做法(接口 + 多个方法):

java

interface PaymentProcessor {
    // 支付
    PaymentResult pay(PaymentRequest request);
    
    // 退款
    RefundResult refund(RefundRequest request);
    
    // 查询
    PaymentStatus query(String paymentId);
    
    // 关闭
    boolean close(String paymentId);
}
​
// 问题:有些支付渠道不支持某些操作
class WechatPaymentProcessor implements PaymentProcessor {
    @Override
    public PaymentResult pay(PaymentRequest request) { /* 实现 */ }
    
    @Override
    public RefundResult refund(RefundRequest request) { /* 实现 */ }
    
    @Override
    public PaymentStatus query(String paymentId) { /* 实现 */ }
    
    @Override
    public boolean close(String paymentId) {
        // 微信支付不支持关闭操作
        throw new UnsupportedOperationException("微信支付不支持关闭操作");
    }
}

函数式接口重构(单一职责原则):

java

// 每个操作都是独立的函数式接口
@FunctionalInterface
interface PayOperation {
    PaymentResult pay(PaymentRequest request);
}
​
@FunctionalInterface
interface RefundOperation {
    RefundResult refund(RefundRequest request);
}
​
@FunctionalInterface
interface QueryOperation {
    PaymentStatus query(String paymentId);
}
​
@FunctionalInterface  
interface CloseOperation {
    boolean close(String paymentId);
}
​
// 支付渠道配置类
class PaymentChannel {
    private final String name;
    private final PayOperation payOperation;
    private final RefundOperation refundOperation;
    private final QueryOperation queryOperation;
    private final CloseOperation closeOperation;
    
    // Builder模式创建
    public static class Builder {
        private String name;
        private PayOperation payOperation;
        private RefundOperation refundOperation = req -> { throw new UnsupportedOperationException("不支持退款"); };
        private QueryOperation queryOperation = id -> { throw new UnsupportedOperationException("不支持查询"); };
        private CloseOperation closeOperation = id -> { throw new UnsupportedOperationException("不支持关闭"); };
        
        public Builder name(String name) { this.name = name; return this; }
        public Builder pay(PayOperation op) { this.payOperation = op; return this; }
        public Builder refund(RefundOperation op) { this.refundOperation = op; return this; }
        public Builder query(QueryOperation op) { this.queryOperation = op; return this; }
        public Builder close(CloseOperation op) { this.closeOperation = op; return this; }
        
        public PaymentChannel build() {
            return new PaymentChannel(this);
        }
    }
    
    private PaymentChannel(Builder builder) {
        this.name = builder.name;
        this.payOperation = builder.payOperation;
        this.refundOperation = builder.refundOperation;
        this.queryOperation = builder.queryOperation;
        this.closeOperation = builder.closeOperation;
    }
    
    // 使用方法引用或Lambda配置
    public static PaymentChannel wechatPay() {
        return new Builder()
            .name("微信支付")
            .pay(WechatPayService::processPayment)
            .refund(WechatPayService::processRefund)
            .query(WechatPayService::queryPayment)
            .build();
    }
    
    public static PaymentChannel cashPay() {
        return new Builder()
            .name("现金支付")
            .pay(CashPayService::receiveCash)
            .close(CashPayService::completeTransaction)
            .build();
    }
}

这个设计的优势:

  1. 可选操作:每个渠道只实现自己支持的操作

  2. 编译时检查:不支持的操作用默认实现

  3. 易于测试:可以轻松Mock每个操作

  4. 运行时配置:可以在运行时动态改变实现

3.2 函数式接口的默认方法:打造流畅的DSL

让我展示一个高级技巧:用默认方法创建领域特定语言(DSL)。

java

@FunctionalInterface
interface ValidationRule<T> {
    ValidationResult validate(T value);
    
    // 组合验证规则:AND
    default ValidationRule<T> and(ValidationRule<T> other) {
        return value -> {
            ValidationResult r1 = this.validate(value);
            if (!r1.isValid()) return r1;
            return other.validate(value);
        };
    }
    
    // 组合验证规则:OR
    default ValidationRule<T> or(ValidationRule<T> other) {
        return value -> {
            ValidationResult r1 = this.validate(value);
            if (r1.isValid()) return r1;
            return other.validate(value);
        };
    }
    
    // 转换验证规则
    default <R> ValidationRule<R> map(Function<R, T> mapper) {
        return value -> this.validate(mapper.apply(value));
    }
    
    // 静态工厂方法
    static <T> ValidationRule<T> of(Predicate<T> predicate, String errorMessage) {
        return value -> predicate.test(value) 
            ? ValidationResult.valid() 
            : ValidationResult.invalid(errorMessage);
    }
}
​
// 使用:创建复杂的验证规则
ValidationRule<String> emailRule = ValidationRule.of(
    email -> email.contains("@") && email.contains("."),
    "邮箱格式不正确"
);
​
ValidationRule<String> lengthRule = ValidationRule.of(
    str -> str.length() >= 6 && str.length() <= 50,
    "长度必须在6-50之间"
);
​
ValidationRule<String> usernameRule = ValidationRule.of(
    name -> name.matches("[a-zA-Z0-9_]+"),
    "用户名只能包含字母、数字和下划线"
);
​
// 组合规则
ValidationRule<String> registrationRule = emailRule
    .and(lengthRule)
    .and(usernameRule);
​
// 验证
ValidationResult result = registrationRule.validate("test@example.com");

这种DSL风格的API:

  1. 可读性强:规则声明像自然语言

  2. 类型安全:编译器能检查类型

  3. 可组合:简单规则组合成复杂规则

  4. 可测试:每个规则都可以独立测试

四、Stream API:从“循环套循环”到“声明式管道”

image-20260201220049717

4.1 真实世界的流操作:多层嵌套数据处理

我接手过一个报表生成模块,原来的代码是这样的:

java

// 生成部门薪资报表
public Map<String, DepartmentReport> generateDepartmentReport(List<Employee> employees) {
    Map<String, DepartmentReport> reportMap = new HashMap<>();
    
    // 按部门分组
    Map<String, List<Employee>> deptGroups = new HashMap<>();
    for (Employee emp : employees) {
        String dept = emp.getDepartment();
        deptGroups.computeIfAbsent(dept, k -> new ArrayList<>()).add(emp);
    }
    
    // 为每个部门生成报表
    for (Map.Entry<String, List<Employee>> entry : deptGroups.entrySet()) {
        String dept = entry.getKey();
        List<Employee> deptEmployees = entry.getValue();
        
        // 计算统计信息
        double totalSalary = 0.0;
        double maxSalary = Double.MIN_VALUE;
        double minSalary = Double.MAX_VALUE;
        Map<String, Integer> titleCount = new HashMap<>();
        
        for (Employee emp : deptEmployees) {
            double salary = emp.getSalary();
            totalSalary += salary;
            maxSalary = Math.max(maxSalary, salary);
            minSalary = Math.min(minSalary, salary);
            
            String title = emp.getTitle();
            titleCount.put(title, titleCount.getOrDefault(title, 0) + 1);
        }
        
        double avgSalary = totalSalary / deptEmployees.size();
        
        // 找出薪资最高的员工
        Employee highestPaid = null;
        for (Employee emp : deptEmployees) {
            if (highestPaid == null || emp.getSalary() > highestPaid.getSalary()) {
                highestPaid = emp;
            }
        }
        
        // 构建报表
        DepartmentReport report = new DepartmentReport();
        report.setDepartmentName(dept);
        report.setEmployeeCount(deptEmployees.size());
        report.setTotalSalary(totalSalary);
        report.setAverageSalary(avgSalary);
        report.setMaxSalary(maxSalary);
        report.setMinSalary(minSalary);
        report.setTitleDistribution(titleCount);
        report.setHighestPaidEmployee(highestPaid.getName());
        
        reportMap.put(dept, report);
    }
    
    return reportMap;
}

这段代码的问题:

  1. 4层嵌套循环和条件判断

  2. 5个临时变量

  3. 多处重复计算

  4. 难以维护和测试

用Stream API重构后:

java

public Map<String, DepartmentReport> generateDepartmentReport(List<Employee> employees) {
    return employees.stream()
        .collect(Collectors.groupingBy(
            Employee::getDepartment,
            Collectors.collectingAndThen(
                Collectors.toList(),
                deptEmployees -> {
                    DoubleSummaryStatistics stats = deptEmployees.stream()
                        .mapToDouble(Employee::getSalary)
                        .summaryStatistics();
                    
                    Map<String, Long> titleDistribution = deptEmployees.stream()
                        .collect(Collectors.groupingBy(
                            Employee::getTitle,
                            Collectors.counting()
                        ));
                    
                    String highestPaid = deptEmployees.stream()
                        .max(Comparator.comparingDouble(Employee::getSalary))
                        .map(Employee::getName)
                        .orElse("无");
                    
                    return DepartmentReport.builder()
                        .departmentName(deptEmployees.get(0).getDepartment())
                        .employeeCount(deptEmployees.size())
                        .totalSalary(stats.getSum())
                        .averageSalary(stats.getAverage())
                        .maxSalary(stats.getMax())
                        .minSalary(stats.getMin())
                        .titleDistribution(titleDistribution)
                        .highestPaidEmployee(highestPaid)
                        .build();
                }
            )
        ));
​

重构带来的好处:

  1. 零循环嵌套:纯声明式

  2. 零临时变量:所有数据在管道中流动

  3. 性能优化:薪资统计一次遍历完成

  4. 并行化容易:只需改成parallelStream()

4.2 高级流操作:处理复杂数据结构

当数据有层级关系时(如部门-组-员工),传统做法是多重循环。但Stream可以处理得更好:

java

// 场景:找出所有部门中薪资最高的员工
public Map<String, Employee> findHighestPaidByDepartment(List<Department> departments) {
    return departments.stream()
        .flatMap(dept -> dept.getGroups().stream()
            .flatMap(group -> group.getEmployees().stream()
                .map(emp -> new AbstractMap.SimpleEntry<>(dept.getName(), emp))
            )
        )
        .collect(Collectors.groupingBy(
            Map.Entry::getKey,
            Collectors.collectingAndThen(
                Collectors.maxBy(Comparator.comparingDouble(e -> e.getValue().getSalary())),
                opt -> opt.map(Map.Entry::getValue).orElse(null)
            )
        ));
}
​
// 更优雅的做法:使用记录类(Record)
record DeptEmployee(String departmentName, Employee employee) {}
​
public Map<String, Employee> findHighestPaidByDepartment(List<Department> departments) {
    return departments.stream()
        .flatMap(dept -> dept.getGroups().stream()
            .flatMap(group -> group.getEmployees().stream()
                .map(emp -> new DeptEmployee(dept.getName(), emp))
            )
        )
        .collect(Collectors.toMap(
            DeptEmployee::departmentName,
            DeptEmployee::employee,
            (e1, e2) -> e1.getSalary() >= e2.getSalary() ? e1 : e2
        ));
}

五、实战:用函数式思维重构GUI事件处理

image-20260201220408159

5.1 从“事件监听器地狱”到“响应式编程”

Swing的事件处理是匿名内部类的重灾区。看看我是如何重构的:

重构前(典型的Swing代码):

java

public class EmployeeFrame extends JFrame {
    private JButton addButton;
    private JButton deleteButton;
    private JButton editButton;
    private JButton searchButton;
    private JTable employeeTable;
    
    public EmployeeFrame() {
        // 初始化组件...
        
        // 添加按钮事件监听器
        addButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                AddEmployeeDialog dialog = new AddEmployeeDialog(EmployeeFrame.this);
                dialog.setVisible(true);
                if (dialog.isConfirmed()) {
                    Employee emp = dialog.getEmployee();
                    employeeService.add(emp);
                    refreshTable();
                }
            }
        });
        
        deleteButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int selectedRow = employeeTable.getSelectedRow();
                if (selectedRow >= 0) {
                    int confirm = JOptionPane.showConfirmDialog(
                        EmployeeFrame.this,
                        "确定要删除选中的员工吗?",
                        "确认删除",
                        JOptionPane.YES_NO_OPTION
                    );
                    if (confirm == JOptionPane.YES_OPTION) {
                        String empId = (String) employeeTable.getValueAt(selectedRow, 0);
                        employeeService.delete(empId);
                        refreshTable();
                    }
                }
            }
        });
        
        // 更多按钮监听器...
    }
}

重构后(命令模式 + Lambda):

java

public class EmployeeFrame extends JFrame {
    
    // 定义命令接口
    @FunctionalInterface
    interface UICommand {
        void execute();
    }
    
    // 命令注册表
    private final Map<String, UICommand> commands = new HashMap<>();
    
    public EmployeeFrame() {
        registerCommands();
        bindEvents();
    }
    
    private void registerCommands() {
        commands.put("add", this::addEmployee);
        commands.put("delete", this::deleteEmployee);
        commands.put("edit", this::editEmployee);
        commands.put("search", this::searchEmployees);
        commands.put("export", this::exportData);
    }
    
    private void bindEvents() {
        addButton.addActionListener(e -> commands.get("add").execute());
        deleteButton.addActionListener(e -> commands.get("delete").execute());
        editButton.addActionListener(e -> commands.get("edit").execute());
        searchButton.addActionListener(e -> commands.get("search").execute());
        exportButton.addActionListener(e -> commands.get("export").execute());
        
        // 快捷键绑定
        getRootPane().registerKeyboardAction(
            e -> commands.get("delete").execute(),
            KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),
            JComponent.WHEN_IN_FOCUSED_WINDOW
        );
    }
    
    private void addEmployee() {
        Optional<Employee> result = new AddEmployeeDialog(this).showDialog();
        result.ifPresent(emp -> {
            employeeService.add(emp);
            refreshTable();
        });
    }
    
    private void deleteEmployee() {
        getSelectedEmployeeId().ifPresent(empId -> {
            boolean confirmed = confirmDialog("确定要删除选中的员工吗?");
            if (confirmed) {
                employeeService.delete(empId);
                refreshTable();
            }
        });
    }
    
    private Optional<String> getSelectedEmployeeId() {
        int row = employeeTable.getSelectedRow();
        return row >= 0 
            ? Optional.of((String) employeeTable.getValueAt(row, 0))
            : Optional.empty();
    }
    
    private boolean confirmDialog(String message) {
        return JOptionPane.showConfirmDialog(
            this, message, "确认操作", JOptionPane.YES_NO_OPTION
        ) == JOptionPane.YES_OPTION;
    }
}

这种架构的优势:

  1. 关注点分离:事件绑定与业务逻辑分离

  2. 易于测试:每个命令都可以单独测试

  3. 动态配置:可以在运行时修改命令实现

  4. 统一错误处理:可以在命令执行处统一添加异常处理

六、性能陷阱与最佳实践

6.1 不要为了函数式而函数式

我见过最糟糕的滥用:

java

// 错误:过度使用Stream
IntStream.range(0, 10).forEach(i -> {
    IntStream.range(0, 10).forEach(j -> {
        IntStream.range(0, 10).forEach(k -> {
            System.out.println(i + ", " + j + ", " + k);
        });
    });
});
​
// 正确:简单的for循环更好
for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 10; j++) {
        for (int k = 0; k < 10; k++) {
            System.out.println(i + ", " + j + ", " + k);
        }
    }
}

我的经验法则:

text

使用Stream API当:
1. 需要复杂的过滤、映射、归约操作
2. 数据量较大,可能从并行化受益
3. 操作是声明式的,可读性更好
​
使用传统循环当:
1. 简单的遍历操作
2. 需要循环控制(break、continue)
3. 性能敏感的底层代码

6.2 并行流的正确使用

java

// 错误:盲目使用并行流
employees.parallelStream()  // 小数据集,并行反而更慢
    .filter(e -> e.getSalary() > 50000)
    .collect(Collectors.toList());
​
// 正确:根据数据量和操作复杂度决定
public List<Employee> filterHighSalary(List<Employee> employees) {
    // 经验法则:数据量大于10000才考虑并行
    Stream<Employee> stream = employees.size() > 10000 
        ? employees.parallelStream() 
        : employees.stream();
    
    return stream
        .filter(e -> e.getSalary() > 50000)
        .collect(Collectors.toList());
}
​
// 特别适合并行的场景:CPU密集型操作
public Map<String, Double> calculateComplexMetrics(List<Employee> employees) {
    return employees.parallelStream()
        .collect(Collectors.groupingByConcurrent(
            Employee::getDepartment,
            Collectors.averagingDouble(emp -> {
                // 复杂的计算逻辑
                double score = calculatePerformanceScore(emp);
                score = adjustScoreByTenure(emp, score);
                score = normalizeScore(score);
                return score;
            })
        ));
}

七、函数式编程的思维模式转变

7.1 从“可变状态”到“不可变数据”

传统Java程序员习惯可变状态:

java

// 传统做法:修改现有对象
public void giveRaise(Employee emp, double percentage) {
    double oldSalary = emp.getSalary();
    double newSalary = oldSalary * (1 + percentage / 100);
    emp.setSalary(newSalary);  // 修改状态
    emp.setLastRaiseDate(new Date());
}
​
// 函数式做法:创建新对象
public Employee withRaise(Employee emp, double percentage) {
    return Employee.builder()
        .id(emp.getId())
        .name(emp.getName())
        .salary(emp.getSalary() * (1 + percentage / 100))  // 新值
        .department(emp.getDepartment())
        .hireDate(emp.getHireDate())
        .lastRaiseDate(new Date())  // 新值
        .build();
}

不可变性的好处:

  1. 线程安全:不需要同步

  2. 可预测性:对象一旦创建就不会改变

  3. 易于调试:没有隐蔽的状态变化

  4. 支持函数式操作:map、filter等操作不改变原数据

7.2 从“异常处理”到“函数式错误处理”

传统异常处理打断程序流:

java

// 传统做法:try-catch块
public Employee findEmployee(String id) {
    try {
        return employeeRepository.findById(id);
    } catch (EntityNotFoundException e) {
        log.error("员工不存在: " + id, e);
        return null;  // 返回null,调用者需要检查
    }
}
​
public void processEmployee(String id) {
    Employee emp = findEmployee(id);
    if (emp != null) {  // 必须检查null
        // 处理员工
    }
}

函数式做法:使用Optional

java

public Optional<Employee> findEmployee(String id) {
    try {
        return Optional.of(employeeRepository.findById(id));
    } catch (EntityNotFoundException e) {
        log.warn("员工不存在: {}", id);
        return Optional.empty();
    }
}
​
public void processEmployee(String id) {
    findEmployee(id)
        .ifPresent(emp -> {
            // 处理员工,不需要null检查
        });
}
​
// 链式操作更优雅
public void processEmployeeWithFallback(String id) {
    String result = findEmployee(id)
        .map(Employee::getName)
        .map(name -> "员工: " + name)
        .orElse("员工不存在");
    
    System.out.println(result);
}

结语:函数式编程不是银弹,而是新视角

三年前,我认为函数式编程只是"语法糖"。今天,我明白它是一种全新的问题解决视角

我的学习路线图:

  1. 阶段1:会用 → 学会Lambda和方法引用的语法

  2. 阶段2:敢用 → 在简单场景中替换匿名内部类

  3. 阶段3:善用 → 用Stream API处理集合操作

  4. 阶段4:妙用 → 用函数式思维设计API和架构

  5. 阶段5:慎用 → 知道什么时候不该用函数式

最后的忠告:

  • 不要重构运行良好的代码:除非有明确收益

  • 保持代码可读性:复杂的链式操作适可而止

  • 渐进式采用:从新代码开始,逐步重构旧代码

  • 团队共识:确保团队其他成员也能理解

函数式编程不会让糟糕的设计变好,但它能让好的设计变得更优雅。它不会自动提升性能,但它能让并发编程更安全。它不是一个必须掌握的技能,但掌握了它会让你在Java世界中多一种强大的工具。

记住:最好的代码不是最"函数式"的代码,而是最清晰地表达意图的代码。函数式编程只是帮助我们达到这个目标的一种方式,而不是目标本身。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐