第68章 Java 8新特性

Lambda表达式、Stream API和函数式编程详解

Java 8新特性概述

Java 8是Java发展史上的一个重要里程碑,引入了许多革命性的新特性,其中最重要的是Lambda表达式和Stream API。这些新特性不仅简化了代码编写,还引入了函数式编程的概念,让Java开发更加高效和优雅。

Lambda表达式

简化匿名函数的编写,使代码更加简洁和可读。支持函数式编程风格,减少样板代码。

Stream API

提供强大的数据处理能力,支持过滤、映射、归约等操作,让集合处理更加优雅。

方法引用

进一步简化Lambda表达式,直接引用已存在的方法,提高代码复用性。

Optional类

优雅地处理空值,避免NullPointerException,提供更安全的编程方式。

函数式接口

支持Lambda表达式的接口,提供丰富的内置函数式接口,简化函数式编程。

新日期时间API

全新设计的日期时间API,线程安全,API设计更加合理和易用。

Lambda表达式详解

Lambda表达式是Java 8最重要的新特性之一,它允许我们将函数作为参数传递,简化了匿名内部类的编写。Lambda表达式的基本语法是:(参数) -> 表达式(参数) -> { 语句块 }

基本语法示例

Lambda表达式基本语法
// 传统匿名内部类方式
Comparator<String> comparator1 = new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
};

// Lambda表达式方式
Comparator<String> comparator2 = (a, b) -> a.compareTo(b);

// 方法引用方式
Comparator<String> comparator3 = String::compareTo;

// 单参数Lambda表达式
List<String> names = Arrays.asList("张三", "李四", "王五");
names.forEach(name -> System.out.println(name));

// 方法引用简化
names.forEach(System.out::println);

Lambda表达式优势

  • 代码简洁:大幅减少样板代码,提高开发效率
  • 可读性强:更接近自然语言的表达方式
  • 函数式编程:支持函数式编程范式
  • 性能优化:编译器可以进行更好的优化

Stream API详解

Stream API是Java 8引入的另一个重要特性,它提供了一种声明式的数据处理方式。Stream不是数据结构,而是数据源的视图,支持函数式编程风格的操作。

Stream操作示例

Stream API基本操作
List<String> words = Arrays.asList("Java", "Python", "JavaScript", "Go", "Rust");

// 过滤、映射、收集
List<String> result = words.stream()
    .filter(word -> word.length() > 4)  // 过滤长度大于4的单词
    .map(String::toUpperCase)           // 转换为大写
    .sorted()                           // 排序
    .collect(Collectors.toList());      // 收集为List

System.out.println(result); // [JAVASCRIPT, PYTHON]

// 数值计算
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

int sum = numbers.stream()
    .filter(n -> n % 2 == 0)    // 过滤偶数
    .mapToInt(Integer::intValue) // 转换为IntStream
    .sum();                      // 求和

System.out.println("偶数和: " + sum); // 偶数和: 30

// 分组操作
Map<Integer, List<String>> groupedByLength = words.stream()
    .collect(Collectors.groupingBy(String::length));

System.out.println(groupedByLength);

Stream使用注意事项

  • Stream只能使用一次,使用后就会关闭
  • 中间操作是惰性的,只有遇到终端操作才会执行
  • 并行流虽然强大,但不是总是更快,需要根据具体情况选择
  • 避免在Stream中修改外部变量,保持函数式编程的纯净性

Optional类详解

Optional是Java 8引入的一个容器类,用于表示可能为空的值。它的设计目的是减少NullPointerException的发生,提供更安全和优雅的空值处理方式。

Optional使用示例

Optional类基本用法
// 创建Optional
Optional<String> optional1 = Optional.of("Hello World");
Optional<String> optional2 = Optional.ofNullable(null);
Optional<String> optional3 = Optional.empty();

// 检查值是否存在
if (optional1.isPresent()) {
    System.out.println("值存在: " + optional1.get());
}

// 使用ifPresent避免显式检查
optional1.ifPresent(value -> System.out.println("值: " + value));

// 提供默认值
String result1 = optional2.orElse("默认值");
String result2 = optional2.orElseGet(() -> "通过Supplier提供的默认值");

// 链式操作
Optional<String> result = Optional.of("java programming")
    .filter(s -> s.length() > 5)
    .map(String::toUpperCase)
    .map(s -> "Language: " + s);

result.ifPresent(System.out::println); // Language: JAVA PROGRAMMING

// 实际应用场景
public Optional<User> findUserById(String id) {
    // 模拟数据库查询
    User user = database.findById(id);
    return Optional.ofNullable(user);
}

// 安全的链式调用
String email = findUserById("123")
    .map(User::getEmail)
    .filter(e -> e.contains("@"))
    .orElse("未找到有效邮箱");

完整代码示例

以下是一个综合运用Java 8新特性的完整示例,展示了Lambda表达式、Stream API和Optional的实际应用:

Java8FeaturesDemo.java
import java.util.*;
import java.util.stream.*;
import java.util.function.*;

public class Java8FeaturesDemo {
    public static void main(String[] args) {
        System.out.println("=== Java 8新特性综合示例 ===");
        
        // 示例数据
        List<Person> people = Arrays.asList(
            new Person("张三", 25, "北京"),
            new Person("李四", 30, "上海"),
            new Person("王五", 28, "广州"),
            new Person("赵六", 35, "深圳")
        );
        
        // Lambda表达式和Stream API综合应用
        people.stream()
            .filter(person -> person.getAge() > 25)  // 过滤年龄大于25的人
            .sorted(Comparator.comparing(Person::getAge))  // 按年龄排序
            .map(Person::getName)  // 提取姓名
            .forEach(System.out::println);  // 输出结果
        
        // 分组统计
        Map<String, Long> cityCount = people.stream()
            .collect(Collectors.groupingBy(
                Person::getCity, 
                Collectors.counting()
            ));
        
        System.out.println("各城市人数统计: " + cityCount);
        
        // Optional使用示例
        Optional<Person> oldestPerson = people.stream()
            .max(Comparator.comparing(Person::getAge));
        
        oldestPerson.ifPresent(person -> 
            System.out.println("年龄最大的人: " + person.getName())
        );
    }
    
    static class Person {
        private String name;
        private int age;
        private String city;
        
        public Person(String name, int age, String city) {
            this.name = name;
            this.age = age;
            this.city = city;
        }
        
        // Getter方法
        public String getName() { return name; }
        public int getAge() { return age; }
        public String getCity() { return city; }
    }
}
💻 查看完整代码 - 在线IDE体验

函数式接口详解

函数式接口是只有一个抽象方法的接口,是Lambda表达式的基础。Java 8在java.util.function包中提供了许多内置的函数式接口。

Predicate<T>

断言型接口,接受一个参数,返回boolean值。常用于过滤操作。

Predicate<String> isEmpty = String::isEmpty;

Function<T,R>

函数型接口,接受一个参数,返回一个结果。常用于映射操作。

Function<String, Integer> length = String::length;

Consumer<T>

消费型接口,接受一个参数,无返回值。常用于遍历操作。

Consumer<String> print = System.out::println;

Supplier<T>

供给型接口,无参数,返回一个结果。常用于延迟计算。

Supplier<Date> now = Date::new;

Java 8新特性最佳实践

使用建议

  • 优先使用方法引用:当Lambda表达式只是调用一个方法时,使用方法引用更简洁
  • 合理使用Stream:对于简单操作,传统循环可能更直观
  • 避免副作用:在Stream操作中避免修改外部状态
  • 谨慎使用并行流:并行流不总是更快,需要根据数据量和操作复杂度选择
  • 善用Optional:在可能返回null的方法中使用Optional

常见陷阱

  • 过度使用Lambda表达式,导致代码可读性下降
  • 在Stream中进行耗时的I/O操作
  • 忽略Stream的惰性特性,导致性能问题
  • 不当使用Optional.get()方法,仍然可能抛出异常
  • 在不合适的场景使用并行流,反而降低性能

本章小结