Lambda、方法引用与Stream流完全指南

Java 8 函数式编程:Lambda、方法引用与Stream流完全指南

前言

Java 8 引入的函数式编程特性是Java语言的一次重大革新,它不仅让代码更加简洁,还带来了全新的编程思维。本文将系统地介绍Lambda表达式、函数式接口、方法引用以及Stream流的核心概念和使用方法,帮助读者快速掌握这些重要特性。


一、Lambda表达式

1.1 什么是Lambda

Lambda表达式是Java 8中引入的一种新语法,它允许我们将函数作为方法参数,或者将代码作为数据来处理。Lambda表达式可以简化匿名内部类的调用,让代码更加简洁。

1.2 初识Lambda

传统方式 vs Lambda方式
java 复制代码
public interface OrderService {
    void addOrder();
}

public class TestOrderService {
    public static void main(String[] args) {
        // 1. 使用实现类调用
        OrderService orderService1 = new OrderServiceImpl();
        orderService1.addOrder();
        
        // 2. 使用匿名内部类调用
        new OrderService() {
            @Override
            public void addOrder() {
                System.out.println("添加订单");
            }
        }.addOrder();
        
        // 3. 使用Lambda表达式
        OrderService orderService = () -> System.out.println("添加订单");
        orderService.addOrder();
    }
}

1.3 Lambda语法规则

Lambda表达式的语法格式如下:

复制代码
(参数列表) -> { 方法体 }

简化规则:

  • 当方法体中只有一条语句时,可以省略花括号 {}
  • 当方法体只有一条 return 语句时,可以省略 return 和花括号
  • 参数类型可以省略,由编译器自动推导

1.4 函数式接口定义

Lambda表达式依赖函数式接口,函数式接口需要满足以下条件:

  1. 接口中只能有一个抽象方法
  2. 可以使用 @FunctionalInterface 注解标记(编译期检查)
  3. 可以通过 default 关键字定义普通方法
  4. 可以定义 Object 类中的方法
java 复制代码
@FunctionalInterface
public interface MyFunctionalInterface {
    void execute();  // 唯一的抽象方法
    
    default void print() {  // 默认方法
        System.out.println("default method");
    }
    
    // Object类中的方法不算抽象方法
    boolean equals(Object obj);
}

二、四大核心函数式接口

Java 8在 java.util.function 包中提供了四大核心函数式接口,几乎满足了所有的使用场景。

2.1 消费型接口 Consumer<T>

特点: 接收一个参数,没有返回值

java 复制代码
public class TestConsumer {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("java", "c", "python", "c++", "VB", "C#");
        
        // 使用Lambda表达式遍历集合
        list.forEach(s -> System.out.println(s));
        
        // 使用方法引用更简洁
        list.forEach(System.out::println);
    }
}

2.2 供给型接口 Supplier<T>

特点: 没有参数,返回一个结果

java 复制代码
public class TestSupplier {
    public static void main(String[] args) {
        Supplier<String> supplier = () -> "hello";
        System.out.println(supplier.get());  // 输出: hello
    }
}

2.3 判断型接口 Predicate<T>

特点: 接收一个参数,返回一个boolean值

java 复制代码
public class TestPredicate {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("hello");
        list.add("java");
        list.add("ok");
        list.add("yes");
        
        System.out.println("删除之前:");
        list.forEach(t -> System.out.println(t));
        
        // 删除包含字母o的元素
        list.removeIf(s -> s.contains("o"));
        
        System.out.println("删除包含o字母的元素之后:");
        list.forEach(t -> System.out.println(t));
    }
}

2.4 功能型接口 Function<T,R>

特点: 接收一个参数,返回一个结果(类型可能不同)

java 复制代码
public class TestFunction {
    public static void main(String[] args) {
        // 实现将字符串首字母转为大写的功能
        Function<String, String> fun = s -> 
            s.substring(0, 1).toUpperCase() + s.substring(1);
        
        System.out.println(fun.apply("hello"));  // 输出: Hello
    }
}

三、方法引用

方法引用是Lambda表达式的简化写法,使用双冒号 :: 来表示。当Lambda体已经有现成的方法实现时,可以直接引用该方法。

3.1 方法引用的类型

3.1.1 对象名引用成员方法
java 复制代码
public class MethodRefObject {
    public void printUpperCase(String str) {
        System.out.println(str.toUpperCase());
    }
}

public class DemoMethodRef {
    private static void printString(Printable lambda) {
        lambda.print("Hello");
    }
    
    public static void main(String[] args) {
        MethodRefObject obj = new MethodRefObject();
        // 使用方法引用
        printString(obj::printUpperCase);
    }
}
3.1.2 类名引用静态方法
java 复制代码
public class DemoMethodRef {
    private static void method(int num, Calcable lambda) {
        System.out.println(lambda.calc(num));
    }
    
    public static void main(String[] args) {
        // Lambda表达式写法
        method(-10, n -> Math.abs(n));
        
        // 方法引用写法(更简洁)
        method(-10, Math::abs);
    }
}
3.1.3 super引用成员方法
java 复制代码
public class Human {
    public void sayHello() {
        System.out.println("Hello!");
    }
}

public class Man extends Human {
    @Override
    public void sayHello() {
        System.out.println("大家好,我是Man!");
    }
    
    public void method(Greetable g) {
        g.greet();
    }
    
    public void show() {
        // 使用super引用父类方法
        method(super::sayHello);
    }
}
3.1.4 this引用成员方法
java 复制代码
public class Husband {
    private void buyHouse() {
        System.out.println("买套房子");
    }
    
    private void marry(Richable lambda) {
        lambda.buy();
    }
    
    public void beHappy() {
        // 使用this引用当前类方法
        marry(this::buyHouse);
    }
}

3.2 构造器引用

类构造器引用
java 复制代码
public class Person {
    private String name;
    
    public Person(String name) {
        this.name = name;
    }
    
    // getter/setter...
}

// 函数式接口
@FunctionalInterface
public interface PersonBuilder {
    Person buildPerson(String name);
}

public class DemoConstructorRef {
    private static void printName(String name, PersonBuilder builder) {
        System.out.println(builder.buildPerson(name).getName());
    }
    
    public static void main(String[] args) {
        // 使用构造器引用
        printName("赵丽颖", Person::new);
    }
}
数组构造器引用
java 复制代码
@FunctionalInterface
public interface ArrayBuilder {
    int[] buildArray(int length);
}

public class DemoArrayRef {
    private static int[] initArray(int length, ArrayBuilder builder) {
        return builder.buildArray(length);
    }
    
    public static void main(String[] args) {
        // 使用数组构造器引用
        int[] array = initArray(10, int[]::new);
    }
}

四、Stream流

Stream是Java 8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,并且支持链式调用。

4.1 什么是Stream

Stream是一个来自数据源的元素队列,支持聚合操作。它有几个关键特点:

  • 元素队列:元素是特定类型的对象,形成一个队列
  • 数据源:流的来源可以是集合、数组等
  • 内部迭代:Stream提供了内部迭代方式,无需显式编写循环
  • 延迟执行:中间操作不会立即执行,只有遇到终结操作才会执行

4.2 获取Stream的三种方式

4.2.1 从Collection获取
java 复制代码
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();

Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream();

Vector<String> vector = new Vector<>();
Stream<String> stream3 = vector.stream();
4.2.2 从Map获取
java 复制代码
Map<String, String> map = new HashMap<>();

// 获取key的Stream
Stream<String> keyStream = map.keySet().stream();

// 获取value的Stream
Stream<String> valueStream = map.values().stream();

// 获取Entry的Stream
Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
4.2.3 从数组获取
java 复制代码
String[] array = {"张无忌", "张翠山", "张三丰", "张一元"};
Stream<String> stream = Stream.of(array);

4.3 Stream常用方法

Stream的方法可以分为两类:

  • 中间操作:返回值类型仍然是Stream,支持链式调用
  • 终结操作:返回值不再是Stream,执行后流结束
4.3.1 forEach:遍历元素
java 复制代码
Stream<String> stream = Stream.of("张无忌", "张三丰", "周芷若");
stream.forEach(name -> System.out.println(name));
4.3.2 filter:过滤元素
java 复制代码
Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
Stream<String> result = original.filter(s -> s.startsWith("张"));
result.forEach(System.out::println);  // 输出: 张无忌 张三丰
4.3.3 map:元素映射
java 复制代码
Stream<String> original = Stream.of("10", "12", "18");
Stream<Integer> result = original.map(str -> Integer.parseInt(str));
result.forEach(System.out::println);  // 输出: 10 12 18
4.3.4 count:统计个数
java 复制代码
Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
Stream<String> result = original.filter(s -> s.startsWith("张"));
System.out.println(result.count());  // 输出: 2
4.3.5 limit:取前n个元素
java 复制代码
Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
Stream<String> result = original.limit(2);
System.out.println(result.count());  // 输出: 2
4.3.6 skip:跳过前n个元素
java 复制代码
Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
Stream<String> result = original.skip(2);
System.out.println(result.count());  // 输出: 1
4.3.7 concat:合并两个流
java 复制代码
Stream<String> streamA = Stream.of("张无忌");
Stream<String> streamB = Stream.of("张翠山");
Stream<String> result = Stream.concat(streamA, streamB);
result.forEach(System.out::println);  // 输出: 张无忌 张翠山

4.4 Stream实战:优雅的集合操作

传统方式 vs Stream方式

需求:从姓名列表中筛选出姓张且名字长度为3的人

传统方式

java 复制代码
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");

List<String> zhangList = new ArrayList<>();
for (String name : list) {
    if (name.startsWith("张")) {
        zhangList.add(name);
    }
}

List<String> shortList = new ArrayList<>();
for (String name : zhangList) {
    if (name.length() == 3) {
        shortList.add(name);
    }
}

for (String name : shortList) {
    System.out.println(name);
}

Stream方式

java 复制代码
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");

// 链式调用,代码简洁明了
list.stream()
    .filter(s -> s.startsWith("张"))
    .filter(s -> s.length() == 3)
    .forEach(System.out::println);

五、总结

本文详细介绍了Java 8函数式编程的核心内容:

  1. Lambda表达式:简化匿名内部类的编写,让代码更简洁
  2. 函数式接口:四大核心接口(Consumer、Supplier、Predicate、Function)满足不同场景
  3. 方法引用:进一步简化Lambda表达式,复用已有方法实现
  4. Stream流:提供函数式编程风格的集合操作,支持链式调用和延迟执行
相关推荐
风筝在晴天搁浅2 分钟前
手撕单例模式
java·开发语言·单例模式
星空ξ4 分钟前
OpenCode + Oh-My-OpenCode 配置指南:集成 GitHub Copilot 模型与 Java LSP (jdtls)
java·github·copilot·opencode·oh-my-opencode
Seven974 分钟前
Tomcat Request请求处理:Container设计
java
逸Y 仙X6 分钟前
文章十五:ElasticSearch 运用ingest加工索引数据
java·大数据·elasticsearch·搜索引擎·全文检索
70asunflower11 分钟前
堆与栈:C 语言内存管理的核心概念
c语言·开发语言
wjs202412 分钟前
Rust 输出到命令行
开发语言
xingpanvip14 分钟前
星盘接口开发文档:日返比接口指南
开发语言·lua
初心未改HD15 分钟前
Go语言Goroutine与Channel深度解析
开发语言·golang
京师20万禁军教头15 分钟前
35面向对象(中级)-编程思想
java
SilentSamsara16 分钟前
Python 并发基础:threading/GIL 与 multiprocessing 的选型逻辑
服务器·开发语言·数据库·vscode·python·pycharm