runnable、callable、consumer、supplier

本文介绍了Java中的函数式接口,包括Runnable、Callable、Consumer及Supplier等,并提供了具体实例说明如何使用这些接口。此外还对比了Java与.NET中相关概念的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java 没有委托的概念; 相反,如果需要一个指向函数的指针,可以创建内联匿名类(或 Java 8 的 lambda 表达式),它们是为此建议设计的某些接口的实现(也称为 Java 8 的功能接口)。 然而,随着 Java 的发展,越来越多的此类接口被添加进来。 虽然它们看起来非常相似且令人困惑,但它们中的每一个都有一个独特的特征,使其与众不同。 可以将它们中的许多映射到 .NET 中的相同类型。 下表列出了一些著名的接口,但还有更多。 例如,为了支持带有两个参数的函数,Java 有另一个接口称为 BiFunction,如果您需要更多参数,则必须创建自己的接口。 请记住,如果唯一的区别是类型参数的数量,Java(最高版本 10)不支持相同的类名。 (在 .NET 中有各种 Func 和 Action 类型,最多有 16 个类型参数。)
在这里插入图片描述
Supplier () -> x
Consumer x -> ()
Callable () -> x throws ex
Runnable () -> ()
Function x -> y


Runnable


Since Java’s early days, multithreading has been a major aspect of the
language. Runnable is the core interface provided for representing
multithreaded tasks, and Java 1.5 provided Callable as an improved
version of Runnable.

Runnable 接口是一个函数式接口,有一个不接受任何参数或返回任何值的 run() 方法。
这适用于我们不寻找线程执行结果的情况,例如传入事件日志记录:

public interface Runnable {
    public void run();
}
public class EventLoggingTask implements  Runnable{
    private Logger logger
      = LoggerFactory.getLogger(EventLoggingTask.class);

    @Override
    public void run() {
        logger.info("Message");
    }
}

在此示例中,线程将仅从队列中读取消息并将其记录在日志文件中。任务没有返回任何值。 我们可以使用 ExecutorService 启动任务:

public void executeTask() {
    executorService = Executors.newSingleThreadExecutor();
    Future future = executorService.submit(new EventLoggingTask());
    executorService.shutdown();
}

Callable


Callable 接口是一个通用接口,包含一个返回通用值 V 的 call() 方法:

public interface Callable<V> {
    V call() throws Exception;
}
public class FactorialTask implements Callable<Integer> {
    int number;

    // standard constructors

    public Integer call() throws InvalidParamaterException {
        int fact = 1;
        // ...
        for(int count = number; count > 1; count--) {
            fact = fact * count;
        }

        return fact;
    }
}
@Test
public void whenTaskSubmitted_ThenFutureResultObtained(){
    FactorialTask task = new FactorialTask(5);
    Future<Integer> future = executorService.submit(task);
 
    assertEquals(120, future.get().intValue());
}

Consumer


从字面意思上我们就可以看得出,consumer接口就是一个消费型的接口,通过传入参数,然后输出值
我们下面就先看一个例子

@Test
public void test_Consumer() {
    //① 使用consumer接口实现方法
    Consumer<String> consumer = new Consumer<String>() {

        @Override
        public void accept(String s) {
            System.out.println(s);
        }
    };
    Stream<String> stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
    stream.forEach(consumer);

    System.out.println("********************");

    //② 使用lambda表达式,forEach方法需要的就是一个Consumer接口
    stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
    Consumer<String> consumer1 = (s) -> System.out.println(s);//lambda表达式返回的就是一个Consumer接口
    stream.forEach(consumer1);
    //更直接的方式
    //stream.forEach((s) -> System.out.println(s));
    System.out.println("********************");

    //③ 使用方法引用,方法引用也是一个consumer
    stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
    Consumer consumer2 = System.out::println;
    stream.forEach(consumer);
    //更直接的方式
    //stream.forEach(System.out::println);
}

lambda 表达式作为 consumer

Consumer<String> consumer1 = (s) -> System.out.println(s);

方法引用作为 consumer

Consumer consumer2 = System.out::println;

Supplier


Supplier 接口是一个供给型的接口,其实,说白了就是一个容器,可以用来存储数据,然后可以供其他方法使用的这么一个接口。

@Test
public void test_Supplier() {
    //① 使用Supplier接口实现方法,只有一个get方法,无参数,返回一个值
    Supplier<Integer> supplier = new Supplier<Integer>() {
        @Override
        public Integer get() {
            //返回一个随机值
            return new Random().nextInt();
        }
    };

    System.out.println(supplier.get());

    System.out.println("********************");

    //② 使用lambda表达式,
    supplier = () -> new Random().nextInt();
    System.out.println(supplier.get());
    System.out.println("********************");

    //③ 使用方法引用
    Supplier<Double> supplier2 = Math::random;
    System.out.println(supplier2.get());
}

看一下这段代码,我们通过创建一个 Supplier 对象,实现了一个 get 方法,这个方法无参数,返回一个值;所以,每次使用这个接口的时候都会返回一个值,并且保存在这个接口中,所以说是一个容器。

Supplier<Integer> supplier = new Supplier<Integer>() {
            @Override
            public Integer get() {
                //返回一个随机值
                return new Random().nextInt();
            }
        };
Supplier<Double> supplier2 = Math::random;
System.out.println(supplier2.get());
<think>好的,我现在需要回答用户关于Java中lambda表达式的问题:“() -> System.out.println("Executing...");是可以被任意的函数式接口接收的吗?”首先,我得理解用户的问题核心。他们可能在学习lambda表达式和函数式接口时,想知道这个特定的lambda是否能适用于所有函数式接口类型。 首先,我需要回忆Java中的函数式接口定义。函数式接口是只有一个抽象方法的接口,比如RunnableCallableSupplier等。这个lambda表达式是一个无参无返回值的表达式,对应的是Runnable接口的run方法,或者类似的函数式接口。 用户的问题是这个lambda是否可以被任意的函数式接口接收。这里的“任意”可能让他们误解为所有函数式接口都能接受这个lambda,但显然不是这样的。例如,一个函数式接口比如Supplier<String>,它的方法签名是get()返回String,而这个lambda没有返回值,所以不能赋值给Supplier<String>。因此,lambda必须匹配目标接口的抽象方法的参数列表和返回类型。 接下来,我需要确定用户可能的误区。可能他们以为只要接口是函数式接口,不管方法签名如何,都可以接收这个lambda。需要澄清这一点,强调方法签名必须兼容。 然后,我需要举例说明不同的函数式接口是否适用。例如,Runnable的run()方法无参无返回值,所以这个lambda可以赋值给Runnable。而其他接口比如ActionListener的actionPerformed方法需要参数,所以不能使用这个lambda。另外,如果接口方法有返回值,但lambda没有返回,也会导致错误。 还要提到@FunctionalInterface注解的作用,以及自动匹配的条件,比如参数数量和类型、返回类型是否兼容。可能需要解释类型推断的过程,编译器如何根据上下文确定目标类型。 用户可能需要知道如何正确地将lambda分配给合适的函数式接口,以及当出现不匹配时的错误类型。例如,尝试将无返回值的lambda赋给需要返回值的接口会导致编译错误。 最后,总结关键点:lambda表达式是否匹配特定的函数式接口取决于方法签名,而不仅仅是接口是否为函数式接口。需要确保参数列表和返回类型一致,或者可以通过上下文推断兼容。</think>在Java中,`() -> System.out.println("Executing...")` 这个Lambda表达式**不能直接被任意的函数式接口接收**,而是必须与**目标函数式接口的抽象方法签名匹配**。以下是详细解释: --- ### 1. Lambda表达式与函数式接口的关系 Lambda表达式需要满足以下条件才能被某个函数式接口接收: - **参数列表**:与函数式接口中唯一抽象方法的参数列表**完全匹配**。 - **返回值类型**:与抽象方法的返回值类型**兼容**(可以一致,或满足类型推断规则)。 --- ### 2. 你的Lambda表达式分析 `() -> System.out.println("Executing...")` 的特性: - **参数列表**:无参数(`()`)。 - **返回值**:无返回值(`void`)。 因此,它只能匹配那些**无参数且返回`void`**的函数式接口。 --- ### 3. 可接收的示例 #### 示例1:`Runnable` 接口 ```java Runnable r = () -> System.out.println("Executing..."); r.run(); // 输出 "Executing..." ``` - `Runnable` 的抽象方法 `run()` 的签名是 `void run()`,与Lambda完全匹配。 #### 示例2:自定义函数式接口 ```java @FunctionalInterface interface MyAction { void execute(); } MyAction action = () -> System.out.println("Executing..."); action.execute(); // 输出 "Executing..." ``` - 自定义接口的抽象方法 `execute()` 与Lambda的签名一致。 --- ### 4. **不能接收的示例** #### 示例1:需要参数的接口(如 `Consumer<T>`) ```java Consumer<String> consumer = () -> System.out.println("Error!"); // 编译错误! ``` - `Consumer<T>` 的抽象方法 `accept(T t)` 需要一个参数,而Lambda没有参数,签名不匹配。 #### 示例2:需要返回值的接口(如 `Supplier<T>`) ```java Supplier<String> supplier = () -> System.out.println("Error!"); // 编译错误! ``` - `Supplier<T>` 的抽象方法 `T get()` 需要返回值,但Lambda没有返回值(`void`),签名不匹配。 --- ### 5. 总结 - Lambda表达式必须与目标函数式接口的抽象方法**参数和返回值完全兼容**。 - **“任意函数式接口”**的说法是错误的,必须满足方法签名匹配。 - 常见的可匹配接口包括:`Runnable`、`ActionListener`(无参无返回值)等。 --- ### 附:函数式接口的匹配规则 | 函数式接口 | 抽象方法签名 | 能否匹配 `() -> ...`? | |-------------------|---------------------|-----------------------| | `Runnable` | `void run()` | ✔️ 是 | | `Supplier<T>` | `T get()` | ❌ 需要返回值 | | `Consumer<T>` | `void accept(T t)` | ❌ 需要参数 | | `ActionListener` | `void actionPerformed(...)` | ✔️ 是(参数需匹配)| 通过这种匹配机制,Java编译器能确保Lambda表达式在类型安全的前提下工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会说话的皮卡丘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值