Java lambda 参数转换

发布于 2022-09-13 01:11:48 字数 347 浏览 12 评论 0

有如下类:

Class A {
    public void a(Consumer<? extends Number> numberConsumer) {};
}

调用时有如下写法:

1. a.a((Long l) -> {});
2. a.a((Consumer<Long>) l -> {});
3. a.a(new Consumer<Long> {...});

那么问题来了:

  1. 写法1和写法2有什么关系,写法1是写法2的简化版?怎么能证明呢?
  2. 还有其它写法?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

暮倦 2022-09-20 01:11:48

2021-08-25 补充

1、2 两种形式,也算不得谁是谁的简写吧,他们只是用不同的方式告诉编译器如何推断/检查类型。

而我觉得更应该讨论的是采用 <? extends Number> 来定义参数类型,纯接口是没问题,但是实现不出来,看下面的代码:

public class Lambda {
    public static void main(String[] args) {
        A a = new A();

        a.a((Long lv) -> {});             // 1
        a.a((Integer iv) -> {});
        a.a((Consumer<Long>) l -> {});    // 2
    }

    static class A {
        public void a(Consumer<? extends Number> numberConsumer) {
            Long l = 1234l;
            Integer i = 1324;
            Number p = l;
            
            // The method numberConsumer(Integer) is undefined for the type Lambda.AJava(67108964)
            numberConsumer(i);
            
            // The method numberConsumer(Long) is undefined for the type Lambda.AJava(67108964)
            numberConsumer(l);
            
            // The method numberConsumer(Number) is undefined for the type Lambda.AJava(67108964)
            numberConsumer(p);
        };
    }
}

A.a 的函数体中,怎么调 numberConsumer 都不对,因为它并不知道 consumer 会处理哪一种类型,所以传哪一种类型进去都不准确。


原回答

先纠个错,public void a (Consumer<Number> numberConsumer) {} 在这里应该不会匹配 Long 型参数的 Lambda,应该是 pubblic void a (Consumer<Long> value) {} 吧。


首先,得明白 Java 的 Labmda 类型是通过 Functional Interface 来定义的。

所谓 Functional Interface,是指只有一个抽象方法的接口(有默认实现的接口方法不算抽象方法),Java 8 以后可以使用 @FunctionalInterface 来注解。那么 Consumer<T>定义是这样:

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
    
    default Consumer<T> andThen(Consumer<? super T> after) {
        // 默认实现
    }
}

观察 accept(T t),它描述了一个泛型 Lambda 类型,参数是 T 类型,不需要返回值。如果把 T 具体化为 Long 就是 (Long v) -> {...} 这样类型的 Lambda,所以

  • a.a((Long long) -> {}) 符合 Consumer<Long>
  • a.a((Consumer<Long>) long -> {}),这里 long 是关键字,不能当参数名使用,可以改为 lv。改过之后,lv 会是 Long 类型,因为用 (Consumer<Long>) 声明了其后 Lambda 的类型。
  • a.a(new Consumer<Long> {...}),这就更直接了,直接从 Consumer<Long> 创建一个匿名类。(注:函数体前应该加 ()。)Lambda 可以说是对单方法接口匿名实现的一个简化语法。

观察下面这段代码:

package playground.problem;

import java.util.function.Consumer;

public class Lambda {
    public static void main(String[] args) {
        A a = new A();

        // Lambda 类型明确,直接匹配 Consumer<Long>
        a.a((Long value) -> {});
        // 显式 Labmda 类型声明
        a.a((Consumer<Long>) lv -> {});
        // 匿名类语法,编译结果会产生 Labmda$1.class
        a.a(new Consumer<Long>() {
            @Override
            public void accept(Long t) {}
        });

        // A::b 是另一个 Lambda 类型相匹配的接口
        a.b((Long value) -> {});
        // A::c 又是一个
        a.c((C) value -> {});
        // 注意:.a()、.b() 和 .c() 的参数类型虽然都匹配同样的 Lambda 形式,但并不是相同的类型
        // 所以显式类型声明为其他类型时会出错

        // 对于 justDoIt,函数签名(参数、返回值等)都匹配,都可以作为参数传入
        a.a(a::justDoIt);
        a.b(a::justDoIt);
        a.c(a::justDoIt);
    }

    static class A {
        public void a(Consumer<Long> numberConsumer) {};

        public void b(B consumer) {}

        public void c(C consumer) {}

        public void justDoIt(Long v) {}
    }

    static interface B {
        void doWith(Long value);
    }

    @FunctionalInterface
    static interface C {
        void doWithLong(Long value);

        default void doWithInteger(int value) {}
    }
}
红焚 2022-09-20 01:11:48
  1. lambda表达式标准格式: (参数类型 参数名称) -> { 代码语句 }
  2. 使用lambda表达式的目的是简化代码,如简化匿名内部类,但是底层实现并不是将lambda编译为匿名内部类,而是使用invokeDynamic。
  3. 参数类型传递,需要传递的参数类型为Consumer<Long>,所以需要定义一个Consumer<Long>,而这个地方根据类型推断推断出来的是Consumer<? extends Number>,与实际类型不匹配,故Consumer<Long>的定义不能简写。
  4. 写法1是lambda定义的标准写法,写法2是算是lambda定义的另一种写法吧。与方法参数无关,只是lambda定义写法不同而已。另外,写法2编译器并没有提示可以简化为写法1。
  5. 匿名内部类的写法编译器会提示可用lambda简化,自动简化后为写法2。
  6. 通过反编译,写法1与写法2字节码一致,写法3为匿名内部类。
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文