CodeGym /课程 /JAVA 25 SELF /函数式接口:@FunctionalInterface

函数式接口:@FunctionalInterface

JAVA 25 SELF
第 20 级 , 课程 3
可用

1. 引言

函数式接口是只包含一个抽象方法(即没有实现的方法)的接口。只有这样的接口才可以用更简洁的方式来表示方法实现——在 Java 中通过 lambda 表达式实现(我们稍后会学习)。

为什么只能有一个方法?

因为函数式接口只描述一个操作。如果方法有两个或更多,就不清楚到底该实现哪个方法了。所以规则很简单:一个接口——一个抽象方法

标准库中的示例

  • Runnable — 用于线程中的任务(void run())
  • Callable<V> — 返回结果的任务(V call())
  • Comparator<T> — 对象比较(int compare(T o1, T o2))
  • Consumer<T> — “消费者”(void accept(T t))
  • Supplier<T> — “供应者”(T get())
  • Function<T, R> — 从 T 到 R 的函数(R apply(T t))
  • Predicate<T> — 条件判断(boolean test(T t))

例如,接口 Runnable

public interface Runnable {
    void run();
}

再看接口 Comparator

public interface Comparator<T> {
    int compare(T o1, T o2);
    // ... 还可以有 default 和 static 方法,但抽象方法只有一个!
}

要点:defaultstatic 方法不算作抽象方法,因此它们可以有任意多个!

2. 注解 @FunctionalInterface

Java 很严格、很讲原则。为避免混淆,它允许用注解 @FunctionalInterface 将接口显式标记为函数式接口。这就像贴上一张“只带一个按钮!”的标签,防止随意添加多余内容。

@FunctionalInterface
public interface Operation {
    int apply(int a, int b);
}

现在,如果你一时忘了又添加了第二个抽象方法,编译器会立刻报错:

@FunctionalInterface
public interface Oops {
    void doIt();
    void doSomethingElse(); // 错误!有两个抽象方法
}

注解是必需的吗?

不是,非必需。如果接口恰好只包含一个抽象方法,它即使没有该注解也仍是函数式接口。但加上注解可以明确你的意图,并保护你免于不小心犯错。

能添加 default 和 static 方法吗?

可以!关键是必须只有一个抽象方法。其他方法都可以是 defaultstatic,数量不限。

示例:

@FunctionalInterface
public interface FancyOperation {
    int apply(int a, int b);

    default void printInfo() {
        System.out.println("我是一个 fancy 操作!");
    }

    static void description() {
        System.out.println("用于算术的函数式接口。");
    }
}

3. 声明与使用示例

假设你想描述一个对两个数字进行运算的操作,可以这样做:

@FunctionalInterface
public interface Operation {
    int apply(int a, int b);
}

现在可以用不同的方式实现该接口。

通过普通类实现

public class SumOperation implements Operation {
    @Override
    public int apply(int a, int b) {
        return a + b;
    }
}

用法:

Operation sum = new SumOperation();
System.out.println(sum.apply(2, 3)); // 5

通过匿名类实现

Operation multiply = new Operation() {
    @Override
    public int apply(int a, int b) {
        return a * b;
    }
};
System.out.println(multiply.apply(2, 3)); // 6

关于 lambda 的说明

从 Java 8 开始,这类接口可以用更简洁的 lambda 表达式来实现——语法更短。我们会在后续几讲学习 lambda,目前只需知道:函数式接口正是为这种便捷用法而设计的。

4. 实践:编写你自己的函数式接口

任务 1:实现你的 Action!

创建接口 Action,接收一个字符串且不返回任何值。通过匿名类实现它,使其将字符串以大写形式打印。

@FunctionalInterface
interface Action {
    void act(String s);
}

public class ActionDemo {
    public static void main(String[] args) {
        Action shout = new Action() {
            @Override
            public void act(String text) {
                System.out.println(text.toUpperCase());
            }
        };

        shout.act("我在学 java!"); // 我在学 JAVA!
    }
}

(稍后我们会看到,使用 lambda 表达式可以写得更短。)

任务 2:数字过滤器

创建接口 NumberPredicate,其方法为 boolean test(int n)。使用匿名类实现偶数性检查。

@FunctionalInterface
interface NumberPredicate {
    boolean test(int n);
}

public class PredicateDemo {
    public static void main(String[] args) {
        NumberPredicate isEven = new NumberPredicate() {
            @Override
            public boolean test(int n) {
                return n % 2 == 0;
            }
        };

        System.out.println(isEven.test(4)); // true
        System.out.println(isEven.test(7)); // false
    }
}

任务 3:使用标准接口

可以直接使用现成的 Predicate<Integer>

import java.util.function.Predicate;

Predicate<Integer> isPositive = new Predicate<Integer>() {
    @Override
    public boolean test(Integer x) {
        return x > 0;
    }
};

System.out.println(isPositive.test(10)); // true
System.out.println(isPositive.test(-5)); // false

表格:标准库中的函数式接口

接口 方法 描述 使用示例
Runnable
void run()
无参数、无返回值的任务 线程、计时器
Callable<V>
V call()
带返回值的任务 线程、ExecutorService
Comparator<T>
int compare(T, T)
比较两个对象 集合排序
Consumer<T>
void accept(T)
“消费者” 遍历集合
Supplier<T>
T get()
“供应者” 延迟初始化、数据生成
Function<T, R>
R apply(T)
从 T 到 R 的函数 数据转换
Predicate<T>
boolean test(T)
条件判断 集合过滤

5. 使用函数式接口时的常见错误

错误 1:添加了第二个抽象方法。 如果接口中有多个抽象方法,它就不再是函数式接口。编译器(尤其是在使用 @FunctionalInterface 时)会立即报告错误。

错误 2:忘了 defaultstatic 方法不算作抽象方法。 可以放心地将它们添加到函数式接口中——这不会违反“只允许一个抽象方法”的规则。

错误 3:方法签名实现不正确。 例如,接口要求两个参数,而你只实现了一个参数的方法。务必检查方法签名。

错误 4:未使用 @FunctionalInterface,结果把接口改坏了。 如果不加注解,可能会不小心添加多余的方法——之后还要费力排查为什么代码不工作。最好总是加上注解,以明确意图。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION