【JDK8新特性】函数式接口Day2

写在前面

欢迎来到JDK8新特性系列教程的第二天!在昨天的学习中,我们了解了Lambda表达式的基本概念和用法。今天,我们将深入探讨Lambda表达式的"灵魂伴侣"------函数式接口

如果说Lambda表达式是JDK8送给Java程序员的"语法糖",那么函数式接口就是承载这颗糖的"容器"。理解函数式接口,是掌握Java函数式编程的关键一步。

准备好了吗?让我们开始今天的学习之旅!


目录

    • 写在前面
    • 一、什么是函数式接口
      • [1.1 定义](#1.1 定义)
      • [1.2 @FunctionalInterface注解](#1.2 @FunctionalInterface注解)
      • [1.3 函数式接口 vs 普通接口](#1.3 函数式接口 vs 普通接口)
    • 二、四大核心函数式接口详解
      • [2.1 Function<T, R> - 函数型接口](#2.1 Function<T, R> - 函数型接口)
      • [2.2 Consumer<T> - 消费型接口](#2.2 Consumer<T> - 消费型接口)
      • [2.3 Supplier<T> - 供给型接口](#2.3 Supplier<T> - 供给型接口)
      • [2.4 Predicate<T> - 断言型接口](#2.4 Predicate<T> - 断言型接口)
      • [2.5 四大核心接口对比表](#2.5 四大核心接口对比表)
    • 三、其他常用函数式接口
      • [3.1 UnaryOperator<T> - 一元操作符](#3.1 UnaryOperator<T> - 一元操作符)
      • [3.2 BinaryOperator<T> - 二元操作符](#3.2 BinaryOperator<T> - 二元操作符)
      • [3.3 BiFunction<T, U, R> - 双参数函数](#3.3 BiFunction<T, U, R> - 双参数函数)
      • [3.4 BiConsumer<T, U> - 双参数消费者](#3.4 BiConsumer<T, U> - 双参数消费者)
      • [3.5 BiPredicate<T, U> - 双参数断言](#3.5 BiPredicate<T, U> - 双参数断言)
      • [3.6 基本类型特化接口](#3.6 基本类型特化接口)
    • 四、函数式接口的链式操作
      • [4.1 Function的链式操作](#4.1 Function的链式操作)
      • [4.2 Consumer的链式操作](#4.2 Consumer的链式操作)
      • [4.3 Predicate的链式操作](#4.3 Predicate的链式操作)
      • [4.4 链式操作对比表](#4.4 链式操作对比表)
    • 五、自定义函数式接口
      • [5.1 基本自定义](#5.1 基本自定义)
      • [5.2 带泛型的自定义接口](#5.2 带泛型的自定义接口)
      • [5.3 带异常的函数式接口](#5.3 带异常的函数式接口)
    • 六、踩坑提醒
      • [6.1 函数式接口只能有一个抽象方法](#6.1 函数式接口只能有一个抽象方法)
      • [6.2 Object类方法的陷阱](#6.2 Object类方法的陷阱)
      • [6.3 Lambda表达式类型推断问题](#6.3 Lambda表达式类型推断问题)
      • [6.4 变量捕获的限制](#6.4 变量捕获的限制)
    • 七、面试高频考点
      • [7.1 函数式接口和抽象类的区别?](#7.1 函数式接口和抽象类的区别?)
      • [7.2 JDK8提供了哪些内置函数式接口?](#7.2 JDK8提供了哪些内置函数式接口?)
      • [7.3 @FunctionalInterface注解的作用?](#7.3 @FunctionalInterface注解的作用?)
      • [7.4 为什么Lambda表达式只能用于函数式接口?](#7.4 为什么Lambda表达式只能用于函数式接口?)
    • 八、总结
    • 参考资料
    • 互动话题

一、什么是函数式接口

1.1 定义

函数式接口(Functional Interface)是指有且仅有一个抽象方法的接口。这种接口可以被隐式转换为Lambda表达式。

1.2 @FunctionalInterface注解

JDK8引入了@FunctionalInterface注解,用于标识一个接口是函数式接口:

java 复制代码
@FunctionalInterface
public interface MyFunctionalInterface {
    // 只有一个抽象方法
    void execute();
    
    // 默认方法可以有多个,不算抽象方法
    default void defaultMethod() {
        System.out.println("这是默认方法");
    }
    
    // 静态方法可以有多个,不算抽象方法
    static void staticMethod() {
        System.out.println("这是静态方法");
    }
}

重要提示@FunctionalInterface注解不是必须的,但强烈建议使用。因为它可以帮助编译器检查接口是否符合函数式接口的定义------如果接口中有多个抽象方法,编译器会报错。

1.3 函数式接口 vs 普通接口

特性 函数式接口 普通接口
抽象方法数量 有且仅有1个 可以有多个
默认方法 可以有 可以有
静态方法 可以有 可以有
能否使用Lambda 可以 不可以
注解标识 建议使用@FunctionalInterface 不需要

二、四大核心函数式接口详解

JDK8在java.util.function包中提供了大量内置的函数式接口,其中最核心的是以下四个:

2.1 Function<T, R> - 函数型接口

定义:接收一个参数T,返回一个结果R。

java 复制代码
@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

应用场景:类型转换、数据映射、提取属性等。

java 复制代码
import java.util.function.Function;

public class FunctionDemo {
    public static void main(String[] args) {
        // 将字符串转换为长度
        Function<String, Integer> lengthFunction = s -> s.length();
        
        System.out.println(lengthFunction.apply("Hello")); // 输出: 5
        System.out.println(lengthFunction.apply("Java8")); // 输出: 5
        
        // 将整数转换为字符串
        Function<Integer, String> intToString = i -> "Number: " + i;
        System.out.println(intToString.apply(100)); // 输出: Number: 100
        
        // 复杂示例:字符串处理管道
        Function<String, String> trim = String::trim;
        Function<String, String> toUpperCase = String::toUpperCase;
        Function<String, String> addPrefix = s -> "[PROCESSED] " + s;
        
        String result = trim.andThen(toUpperCase).andThen(addPrefix).apply("  hello world  ");
        System.out.println(result); // 输出: [PROCESSED] HELLO WORLD
    }
}

2.2 Consumer - 消费型接口

定义:接收一个参数T,不返回任何结果(返回void)。

java 复制代码
@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

应用场景:打印日志、发送消息、修改对象状态等副作用操作。

java 复制代码
import java.util.function.Consumer;
import java.util.Arrays;
import java.util.List;

public class ConsumerDemo {
    public static void main(String[] args) {
        // 简单的打印消费者
        Consumer<String> printConsumer = s -> System.out.println(s);
        printConsumer.accept("Hello Consumer!");
        
        // 对象处理消费者
        Consumer<List<Integer>> clearList = list -> list.clear();
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        // 注意:Arrays.asList返回的列表不能修改,这里仅作示例
        
        // 实际应用:遍历集合并处理
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
        Consumer<String> greet = name -> System.out.println("Hello, " + name + "!");
        names.forEach(greet);
        
        // 输出:
        // Hello, Alice!
        // Hello, Bob!
        // Hello, Charlie!
        
        // 多个消费者的组合
        Consumer<String> printUpper = s -> System.out.println(s.toUpperCase());
        Consumer<String> printLength = s -> System.out.println("Length: " + s.length());
        
        Consumer<String> combined = printUpper.andThen(printLength);
        combined.accept("Java");
        // 输出:
        // JAVA
        // Length: 4
    }
}

2.3 Supplier - 供给型接口

定义:不接收任何参数,返回一个结果T。

java 复制代码
@FunctionalInterface
public interface Supplier<T> {
    T get();
}

应用场景:延迟加载、工厂模式、生成数据等。

java 复制代码
import java.util.function.Supplier;
import java.util.Random;

public class SupplierDemo {
    public static void main(String[] args) {
        // 生成随机数
        Supplier<Integer> randomSupplier = () -> new Random().nextInt(100);
        System.out.println(randomSupplier.get()); // 输出: 0-99之间的随机数
        System.out.println(randomSupplier.get()); // 输出: 另一个随机数
        
        // 延迟创建对象
        Supplier<StringBuilder> sbSupplier = StringBuilder::new;
        StringBuilder sb = sbSupplier.get();
        sb.append("Hello").append(" ").append("World");
        System.out.println(sb.toString()); // 输出: Hello World
        
        // 实际应用:Optional的orElseGet
        String value = null;
        String result = java.util.Optional.ofNullable(value)
            .orElseGet(() -> "Default Value");
        System.out.println(result); // 输出: Default Value
        
        // 工厂模式应用
        Supplier<Person> personFactory = () -> new Person("Unknown", 0);
        Person person = personFactory.get();
        System.out.println(person);
    }
}

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

2.4 Predicate - 断言型接口

定义:接收一个参数T,返回boolean结果。

java 复制代码
@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

应用场景:条件判断、过滤数据、验证输入等。

java 复制代码
import java.util.function.Predicate;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class PredicateDemo {
    public static void main(String[] args) {
        // 基本用法
        Predicate<Integer> isEven = n -> n % 2 == 0;
        System.out.println(isEven.test(4)); // 输出: true
        System.out.println(isEven.test(5)); // 输出: false
        
        // 字符串判断
        Predicate<String> isNotEmpty = s -> s != null && !s.isEmpty();
        System.out.println(isNotEmpty.test("Hello")); // 输出: true
        System.out.println(isNotEmpty.test(""));      // 输出: false
        
        // 实际应用:过滤集合
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> evenNumbers = numbers.stream()
            .filter(isEven)
            .collect(Collectors.toList());
        System.out.println(evenNumbers); // 输出: [2, 4, 6, 8, 10]
        
        // 组合多个条件
        Predicate<Integer> greaterThan5 = n -> n > 5;
        Predicate<Integer> lessThan10 = n -> n < 10;
        
        // 与操作
        Predicate<Integer> between5And10 = greaterThan5.and(lessThan10);
        System.out.println(between5And10.test(7));  // 输出: true
        System.out.println(between5And10.test(11)); // 输出: false
        
        // 或操作
        Predicate<Integer> lessThan3OrGreaterThan8 = 
            ((Predicate<Integer>) n -> n < 3).or(n -> n > 8);
        System.out.println(lessThan3OrGreaterThan8.test(2));  // 输出: true
        System.out.println(lessThan3OrGreaterThan8.test(5));  // 输出: false
        System.out.println(lessThan3OrGreaterThan8.test(9));  // 输出: true
    }
}

2.5 四大核心接口对比表

接口名 方法签名 输入 输出 典型用途
Function<T,R> R apply(T t) T R 类型转换、数据映射
Consumer void accept(T t) T void 副作用操作、处理数据
Supplier T get() T 延迟加载、数据生成
Predicate boolean test(T t) T boolean 条件判断、数据过滤

三、其他常用函数式接口

除了四大核心接口,JDK8还提供了许多其他实用的函数式接口:

3.1 UnaryOperator - 一元操作符

UnaryOperator<T>Function<T, T>的特化,输入和输出类型相同。

java 复制代码
import java.util.function.UnaryOperator;

public class UnaryOperatorDemo {
    public static void main(String[] args) {
        // 字符串转大写
        UnaryOperator<String> toUpperCase = s -> s.toUpperCase();
        System.out.println(toUpperCase.apply("hello")); // 输出: HELLO
        
        // 数字平方
        UnaryOperator<Integer> square = n -> n * n;
        System.out.println(square.apply(5)); // 输出: 25
        
        // List的replaceAll使用UnaryOperator
        java.util.List<String> list = new java.util.ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.replaceAll(s -> s.toUpperCase());
        System.out.println(list); // 输出: [A, B, C]
    }
}

3.2 BinaryOperator - 二元操作符

BinaryOperator<T>BiFunction<T, T, T>的特化,两个输入和一个输出类型相同。

java 复制代码
import java.util.function.BinaryOperator;

public class BinaryOperatorDemo {
    public static void main(String[] args) {
        // 两数相加
        BinaryOperator<Integer> add = (a, b) -> a + b;
        System.out.println(add.apply(3, 5)); // 输出: 8
        
        // 字符串拼接
        BinaryOperator<String> concat = (s1, s2) -> s1 + " " + s2;
        System.out.println(concat.apply("Hello", "World")); // 输出: Hello World
        
        // 求最大值
        BinaryOperator<Integer> max = BinaryOperator.maxBy(Integer::compare);
        System.out.println(max.apply(10, 20)); // 输出: 20
        
        // 求最小值
        BinaryOperator<Integer> min = BinaryOperator.minBy(Integer::compare);
        System.out.println(min.apply(10, 20)); // 输出: 10
    }
}

3.3 BiFunction<T, U, R> - 双参数函数

接收两个不同类型参数,返回一个结果。

java 复制代码
import java.util.function.BiFunction;

public class BiFunctionDemo {
    public static void main(String[] args) {
        // 拼接字符串和数字
        BiFunction<String, Integer, String> format = 
            (prefix, number) -> prefix + ": " + number;
        System.out.println(format.apply("Score", 95)); // 输出: Score: 95
        
        // 创建对象
        BiFunction<String, Integer, Person> createPerson = Person::new;
        Person person = createPerson.apply("Alice", 25);
        System.out.println(person); // 输出: Person{name='Alice', age=25}
    }
}

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

3.4 BiConsumer<T, U> - 双参数消费者

接收两个参数,不返回结果。

java 复制代码
import java.util.function.BiConsumer;
import java.util.Map;
import java.util.HashMap;

public class BiConsumerDemo {
    public static void main(String[] args) {
        // 打印键值对
        BiConsumer<String, Integer> printEntry = (key, value) -> 
            System.out.println(key + " = " + value);
        printEntry.accept("Age", 25); // 输出: Age = 25
        
        // 遍历Map
        Map<String, Integer> scores = new HashMap<>();
        scores.put("Math", 95);
        scores.put("English", 88);
        scores.put("Science", 92);
        
        scores.forEach((subject, score) -> 
            System.out.println(subject + ": " + score + "分")
        );
    }
}

3.5 BiPredicate<T, U> - 双参数断言

接收两个参数,返回boolean。

java 复制代码
import java.util.function.BiPredicate;

public class BiPredicateDemo {
    public static void main(String[] args) {
        // 判断字符串长度是否等于指定值
        BiPredicate<String, Integer> lengthEquals = 
            (str, len) -> str.length() == len;
        System.out.println(lengthEquals.test("Hello", 5)); // 输出: true
        System.out.println(lengthEquals.test("Hello", 4)); // 输出: false
        
        // 判断数字是否在范围内
        BiPredicate<Integer, int[]> inRange = (num, range) -> 
            num >= range[0] && num <= range[1];
        System.out.println(inRange.test(5, new int[]{1, 10})); // 输出: true
    }
}

3.6 基本类型特化接口

为了避免装箱拆箱的性能开销,JDK8为基本类型提供了特化版本:

接口类型 int特化 long特化 double特化
Function IntFunction LongFunction DoubleFunction
Consumer IntConsumer LongConsumer DoubleConsumer
Supplier IntSupplier LongSupplier DoubleSupplier
Predicate IntPredicate LongPredicate DoublePredicate
UnaryOperator IntUnaryOperator LongUnaryOperator DoubleUnaryOperator
BinaryOperator IntBinaryOperator LongBinaryOperator DoubleBinaryOperator
java 复制代码
import java.util.function.IntPredicate;
import java.util.function.IntFunction;

public class PrimitiveDemo {
    public static void main(String[] args) {
        // 使用int特化避免装箱
        IntPredicate isPositive = n -> n > 0;
        System.out.println(isPositive.test(5));  // 输出: true
        System.out.println(isPositive.test(-3)); // 输出: false
        
        IntFunction<String> intToString = n -> "Number: " + n;
        System.out.println(intToString.apply(42)); // 输出: Number: 42
        
        // 性能对比:使用特化版本比Predicate<Integer>更高效
    }
}

四、函数式接口的链式操作

函数式接口支持链式调用,可以组合多个操作形成复杂的数据处理管道。

4.1 Function的链式操作

java 复制代码
import java.util.function.Function;

public class FunctionChaining {
    public static void main(String[] args) {
        Function<String, String> trim = String::trim;
        Function<String, String> toLowerCase = String::toLowerCase;
        Function<String, Integer> length = String::length;
        
        // andThen: 先执行当前函数,再执行参数函数
        String result1 = trim.andThen(toLowerCase).apply("  HELLO WORLD  ");
        System.out.println(result1); // 输出: hello world
        
        // compose: 先执行参数函数,再执行当前函数
        String result2 = toLowerCase.compose(trim).apply("  HELLO WORLD  ");
        System.out.println(result2); // 输出: hello world
        
        // 链式获取长度
        Integer len = trim.andThen(toLowerCase).andThen(length).apply("  HELLO  ");
        System.out.println(len); // 输出: 5
        
        // identity: 返回输入本身
        Function<String, String> identity = Function.identity();
        System.out.println(identity.apply("test")); // 输出: test
    }
}

4.2 Consumer的链式操作

java 复制代码
import java.util.function.Consumer;

public class ConsumerChaining {
    public static void main(String[] args) {
        Consumer<String> print = s -> System.out.println("Print: " + s);
        Consumer<String> log = s -> System.out.println("Log: " + s);
        Consumer<String> save = s -> System.out.println("Save: " + s);
        
        // andThen: 依次执行多个消费者
        Consumer<String> combined = print.andThen(log).andThen(save);
        combined.accept("Hello");
        // 输出:
        // Print: Hello
        // Log: Hello
        // Save: Hello
    }
}

4.3 Predicate的链式操作

java 复制代码
import java.util.function.Predicate;
import java.util.List;
import java.util.Arrays;
import java.util.stream.Collectors;

public class PredicateChaining {
    public static void main(String[] args) {
        Predicate<Integer> greaterThan5 = n -> n > 5;
        Predicate<Integer> lessThan20 = n -> n < 20;
        Predicate<Integer> isEven = n -> n % 2 == 0;
        
        // and: 逻辑与
        Predicate<Integer> between5And20 = greaterThan5.and(lessThan20);
        
        // or: 逻辑或
        Predicate<Integer> lessThan5OrEven = lessThan20.negate().or(isEven);
        
        // negate: 逻辑非
        Predicate<Integer> notEven = isEven.negate();
        
        // 复杂组合
        Predicate<Integer> complex = greaterThan5
            .and(lessThan20)
            .and(isEven);
        
        List<Integer> numbers = Arrays.asList(1, 6, 8, 12, 15, 22, 24);
        List<Integer> filtered = numbers.stream()
            .filter(complex)
            .collect(Collectors.toList());
        System.out.println(filtered); // 输出: [6, 8, 12]
        
        // isEqual: 静态方法,判断是否等于指定值
        Predicate<String> isJava = Predicate.isEqual("Java");
        System.out.println(isJava.test("Java"));   // 输出: true
        System.out.println(isJava.test("Python")); // 输出: false
    }
}

4.4 链式操作对比表

接口 方法 作用 示例
Function andThen 先执行当前,再执行参数 f.andThen(g).apply(x) = g(f(x))
Function compose 先执行参数,再执行当前 f.compose(g).apply(x) = f(g(x))
Function identity 返回输入本身 Function.identity().apply(x) = x
Consumer andThen 依次执行多个消费者 c1.andThen(c2).accept(x)
Predicate and 逻辑与 p1.and(p2).test(x)
Predicate or 逻辑或 p1.or(p2).test(x)
Predicate negate 逻辑非 p.negate().test(x)
Predicate isEqual 判断是否相等 Predicate.isEqual(obj)

五、自定义函数式接口

虽然JDK8提供了丰富的内置函数式接口,但在某些场景下,我们仍需要自定义函数式接口。

5.1 基本自定义

java 复制代码
@FunctionalInterface
public interface Calculator {
    int calculate(int a, int b);
}

// 使用
public class CustomFunctionalInterface {
    public static void main(String[] args) {
        Calculator add = (a, b) -> a + b;
        Calculator subtract = (a, b) -> a - b;
        Calculator multiply = (a, b) -> a * b;
        
        System.out.println(add.calculate(5, 3));      // 输出: 8
        System.out.println(subtract.calculate(5, 3)); // 输出: 2
        System.out.println(multiply.calculate(5, 3)); // 输出: 15
    }
}

5.2 带泛型的自定义接口

java 复制代码
@FunctionalInterface
public interface Validator<T> {
    boolean validate(T value);
    
    // 默认方法提供组合功能
    default Validator<T> and(Validator<T> other) {
        return value -> this.validate(value) && other.validate(value);
    }
    
    default Validator<T> or(Validator<T> other) {
        return value -> this.validate(value) || other.validate(value);
    }
}

// 使用
public class ValidatorDemo {
    public static void main(String[] args) {
        Validator<String> notNull = s -> s != null;
        Validator<String> notEmpty = s -> !s.isEmpty();
        Validator<String> minLength3 = s -> s.length() >= 3;
        
        Validator<String> validName = notNull.and(notEmpty).and(minLength3);
        
        System.out.println(validName.validate("John"));  // 输出: true
        System.out.println(validName.validate("Jo"));    // 输出: false
        System.out.println(validName.validate(""));      // 输出: false
        System.out.println(validName.validate(null));    // 输出: false
    }
}

5.3 带异常的函数式接口

JDK8的函数式接口不允许抛出受检异常,需要自定义:

java 复制代码
@FunctionalInterface
public interface ThrowingFunction<T, R, E extends Exception> {
    R apply(T t) throws E;
}

@FunctionalInterface
public interface ThrowingConsumer<T, E extends Exception> {
    void accept(T t) throws E;
}

// 包装工具类
public class LambdaExceptionUtil {
    public static <T, R, E extends Exception> Function<T, R> 
            wrap(ThrowingFunction<T, R, E> throwingFunction) {
        return t -> {
            try {
                return throwingFunction.apply(t);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }
}

// 使用
public class ExceptionDemo {
    public static void main(String[] args) {
        java.util.List<String> list = java.util.Arrays.asList("1", "2", "abc");
        
        // 使用包装后的函数处理可能抛出异常的代码
        list.stream()
            .map(LambdaExceptionUtil.wrap(Integer::parseInt))
            .forEach(System.out::println);
    }
}

六、踩坑提醒

6.1 函数式接口只能有一个抽象方法

坑点:默认方法和静态方法不算抽象方法,但如果接口继承了其他接口的方法,可能导致抽象方法数量超过1个。

java 复制代码
// 错误示例
@FunctionalInterface
public interface BadInterface extends Runnable {
    void run();  // 来自Runnable的抽象方法
    void doSomething();  // 另一个抽象方法 - 编译错误!
}

// 正确示例
@FunctionalInterface
public interface GoodInterface extends Runnable {
    // 只继承run(),没有新增抽象方法
    default void doSomething() {
        System.out.println("Doing something");
    }
}

6.2 Object类方法的陷阱

如果函数式接口声明了Object类中的方法(如equals、toString、hashCode),这些方法不算作抽象方法:

java 复制代码
@FunctionalInterface
public interface MyInterface {
    void execute();  // 唯一的抽象方法
    
    // 以下方法不会导致编译错误
    boolean equals(Object obj);  // Object类的方法
    String toString();           // Object类的方法
    int hashCode();              // Object类的方法
}

6.3 Lambda表达式类型推断问题

java 复制代码
import java.util.function.Function;
import java.util.function.BiFunction;

public class TypeInferencePitfall {
    public static void main(String[] args) {
        // 编译错误:类型推断失败
        // var func = (String s) -> s.length();
        
        // 正确做法:显式声明类型
        Function<String, Integer> func = s -> s.length();
        
        // 或者使用类型推断(省略参数类型)
        Function<String, Integer> func2 = (s) -> s.length();
        
        // 注意:只有一个参数时括号可以省略
        Function<String, Integer> func3 = s -> s.length();
    }
}

6.4 变量捕获的限制

Lambda表达式只能捕获final或 effectively final 的局部变量:

java 复制代码
public class VariableCapture {
    public static void main(String[] args) {
        int count = 0;
        
        // 编译错误:count必须是final或effectively final
        // Runnable r = () -> System.out.println(count++);
        
        // 正确做法:使用数组或原子类包装
        int[] counter = {0};
        Runnable r = () -> System.out.println(counter[0]++);
        r.run(); // 输出: 0
        r.run(); // 输出: 1
        
        // 更好的做法:使用原子类
        java.util.concurrent.atomic.AtomicInteger atomicCount = 
            new java.util.concurrent.atomic.AtomicInteger(0);
        Runnable r2 = () -> System.out.println(atomicCount.getAndIncrement());
        r2.run(); // 输出: 0
        r2.run(); // 输出: 1
    }
}

七、面试高频考点

7.1 函数式接口和抽象类的区别?

特性 函数式接口 抽象类
继承 可以多实现 只能单继承
方法 只能有1个抽象方法 可以有多个抽象方法
状态 不能有实例字段 可以有实例字段
构造器 没有
访问修饰符 默认public 可以自定义
使用场景 行为参数化 代码复用、模板方法

7.2 JDK8提供了哪些内置函数式接口?

JDK8在java.util.function包中提供了43个函数式接口,主要包括:

核心接口(4个)

  • Function<T,R>:类型转换
  • Consumer:消费数据
  • Supplier:提供数据
  • Predicate:条件判断

扩展接口

  • UnaryOperator:一元操作
  • BinaryOperator:二元操作
  • BiFunction<T,U,R>:双参数函数
  • BiConsumer<T,U>:双参数消费者
  • BiPredicate<T,U>:双参数断言

基本类型特化(约30+个)

  • IntFunction、LongFunction、DoubleFunction
  • IntConsumer、LongConsumer、DoubleConsumer
  • IntPredicate、LongPredicate、DoublePredicate
  • 等等...

7.3 @FunctionalInterface注解的作用?

  1. 标识作用:明确表示这是一个函数式接口
  2. 编译检查:编译器会检查接口是否只有一个抽象方法
  3. 文档说明:帮助其他开发者理解接口的设计意图
  4. 生成文档:在Javadoc中生成特殊标记

7.4 为什么Lambda表达式只能用于函数式接口?

Lambda表达式的本质是匿名函数,它没有名称、没有类型,只有参数列表和方法体。函数式接口只有一个抽象方法,因此Lambda表达式可以明确地对应该方法。如果接口有多个抽象方法,编译器就无法确定Lambda表达式对应哪个方法。


八、总结

今天我们深入学习了JDK8的函数式接口,主要内容包括:

  1. 函数式接口的定义:有且仅有一个抽象方法的接口
  2. 四大核心接口:Function、Consumer、Supplier、Predicate
  3. 其他常用接口:UnaryOperator、BinaryOperator、BiFunction等
  4. 链式操作:andThen、compose、and、or、negate等方法的使用
  5. 自定义函数式接口:满足特定业务需求
  6. 常见坑点:抽象方法数量限制、变量捕获限制等

下一步预告

在Day3中,我们将学习方法引用与构造器引用,这是Lambda表达式的语法糖,能让代码更加简洁优雅。我们将探讨:

  • 方法引用的四种类型
  • 方法引用与Lambda的区别
  • 构造器引用的使用场景

敬请期待!


参考资料

  1. Java 8 函数式接口官方文档
  2. Java 8 Functional Interfaces Tutorial

互动话题

  1. 你在实际项目中使用过哪些函数式接口?遇到过什么坑吗?
  2. 你觉得函数式编程给Java带来了哪些改变?
  3. 对于自定义函数式接口,你有什么好的设计经验分享?

欢迎在评论区留言讨论,点赞收藏不迷路,我们Day3见!

相关推荐
独自破碎E2 小时前
机器人Java后端算法笔试题解析
java·windows·算法
Trouvaille ~2 小时前
【Redis篇】Redis 安装与启动:快速搭建一个 Redis 环境
数据库·redis·后端·ubuntu·缓存·环境搭建·安装教程
Bat U2 小时前
JavaEE|JVM
java·jvm·java-ee
计算机安禾2 小时前
【c++面向对象编程】第45篇:萃取(Traits)技术与策略类:STL源码中的智慧
开发语言·c++
Mahir082 小时前
Spring Boot 自动装配深度解密:从原理到自定义 Starter 实战
java·spring boot·后端·自动装配·自定义starter·大厂面试题
淘源码d2 小时前
产科系统源码,数字产科源码,Java(后端) + Vue + ElementUI(前端) + MySQL(数据库),确保系统稳定性与扩展性。
java·源码·数字产科·产科系统·智能化孕产服务·高危五色预警·智慧产科
wand codemonkey3 小时前
SpringbootWeb【入门】+MySQL【安装】+【DataDrip安装 】+【连接MySQL】
java·mysql·mybatis
Highcharts.js4 小时前
缺失数据可视化图表开发实战|Highcharts创建人员出生统计面积图表示例
开发语言·前端·javascript·信息可视化·highcharts·图表开发
测试员周周9 小时前
【Appium 系列】第16节-WebView-H5上下文切换 — 混合应用的自动化难点
运维·开发语言·人工智能·功能测试·appium·自动化·测试用例