当前位置: 首页 > 知识库问答 >
问题:

Java如何知道用lambda表达式调用哪个重载方法?(供应商,消费者,可调用,...)

卞琨
2023-03-14

首先,我不知道如何得体地表达这个问题,所以这是我的建议。

假设我们有以下重载方法:

void execute(Callable<Void> callable) {
    try {
        callable.call();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

<T> T execute(Supplier<T> supplier) {
    return supplier.get();
}

void execute(Runnable runnable) {
    runnable.run();
}

离开这张桌子,我又问了一个问题

Supplier       ()    -> x
Consumer       x     -> ()
BiConsumer     x, y  -> ()
Callable       ()    -> x throws ex
Runnable       ()    -> ()
Function       x     -> y
BiFunction     x,y   -> z
Predicate      x     -> boolean
UnaryOperator  x1    -> x2
BinaryOperator x1,x2 -> x3

以下是我在当地得到的结果:

// Runnable -> expected as this is a plain void  
execute(() -> System.out.println()); 

// Callable -> why is it not a Supplier? It does not throw any exceptions..
execute(() -> null);

// Supplier -> this returns an Object, but how is that different from returning null?
execute(() -> new Object());

// Callable -> because it can throw an exception, right?
execute(() -> {throw new Exception();});

编译器如何知道调用哪个方法?例如,它如何区分什么是可调用的和什么是可运行的

共有3个答案

刘英彦
2023-03-14

除了()-

()-

CallableSupplier更具体(已经有评论建议)。Java文档声明,如果可能,它会选择最具体的类型:

类型推断是Java编译器查看每个方法调用和相应声明的能力,以确定使调用适用的类型参数。推理算法确定参数的类型,以及(如果可用)分配或返回结果的类型。最后,推理算法试图找到适用于所有参数的最具体类型。

端木朝
2023-03-14
// Callable -> why is it not a Supplier? It does not throw any exceptions..
execute(() -> null);

这是因为都是可调用的

显示execute(可调用

设m1为第一种方法,m2为第二种方法。其中m2具有类型参数P1。。。,让α1。。。,αp是推理变量,θ是代换[P1:=α1,…,Pp:=αp]。

让我们。。。,ek可以是相应调用的参数表达式。然后:

  • 如果m1和m2通过严格或宽松的调用适用(§15.12.2.2,§15.12.2.3),那么让S1。。。,Sk是m1的形式参数类型,设T1。。。,Tk是θ应用于m2的形式参数类型的结果
  • […]

因此,m1execute(可调用的)

现在只引用与问题相关的部分:

确定m1是否比m2更具体的过程如下:

>

  • 首先,按照§18.1.3中的规定,从P1,..., Pp的声明边界构造初始约束集B。

    第二,我(1)≤ 我≤ k) ,将生成一组约束公式或边界。

    否则,Ti是功能接口的参数化,即必须确定Si是否满足以下五个条件:

    [...]

    如果所有五个条件都为真,则生成以下约束公式或界(其中U1... Uk和R1是Si捕获的函数类型的参数类型和返回类型,V1... Vk和R2是Ti函数类型的参数类型和返回类型):

    • 如果ei是显式类型的lambda表达式:
      • […]
      • 否则,èR1

      请注意,没有参数的lambda是显式类型的lambda。

      将此应用回您的问题,R1VoidR2对象,约束是èR1

      最后:

      第四,生成的边界和约束公式被缩减并与B合并,以生成一个边界集B'。

      如果B'不包含界假,并且B'中所有推理变量的解析成功,那么m1比m2更具体。

      由于约束无效

      // Supplier -> this returns an Object, but how is that different from returning null?
      execute(() -> new Object());
      

      在这种情况下,只有供应商

      // Callable -> because it can throw an exception, right?
      execute(() -> {throw new Exception();});
      

      不完全是。抛出异常使可调用

      对于表达式e,如果T不是S的子类型,且以下情况之一为真(其中U1…Uk和R1是S捕获的函数类型的参数类型和返回类型,V1…Vk和R2是T的函数类型的参数类型和返回类型),则函数接口类型S比函数接口类型T更具体:

      • 如果e是一个显式类型的lambda表达式(§15.27.1),则以下情况之一为真:
        • R2是无效的

        基本上,对于显式类型的lambdas,任何非-返回函数接口类型都比-返回函数接口类型更具体。

  • 邹宏峻
    2023-03-14

    我相信我已经找到了官方文件中描述的地方,尽管有点难读。

    这里提到:

    15.27.3. Lambda表达式的类型

    请注意,虽然在严格的调用上下文中不允许装箱,但始终允许装箱lambda结果表达式——也就是说,结果表达式出现在赋值上下文中,而不管包含lambda表达式的上下文是什么。但是,如果显式类型的lambda表达式是重载方法的参数,则最具体的检查(§15.12.2.5)首选避免装箱或取消装箱lambda结果的方法签名。

    然后这里(15.12.2.5)分析性地描述了如何选择最具体的方法。

    根据这个例子

    一个适用方法m1比另一个适用方法m2更具体,对于参数表达式e1。。。,ek,如果以下任何一项为真:

    对于参数表达式e1,...,ek,m1被推断为比m2更具体

    所以

    // Callable -> why is it not a Supplier?
    execute(() -> null);   <-- Callable shall be picked from 2 options as M2 is generic and M1 is inferred to be more specific
    
    void execute(Callable<Void> callable) {  // <------ M1 
       try {
        callable.call();
      } catch (Exception e) {
          e.printStackTrace();
      }
    }
    
    
     <T> T execute(Supplier<T> supplier) {  // <------ M2 is Generic
        return supplier.get();
     }
    

    M1被推断为更具体的原因可以从这里描述的过程中追溯(18.5.4更具体的方法推断)

     类似资料:
    • 在JAVA8中,可以用lambda表达式替换内部类。 jvm怎么知道,这个lambda应该重写正确的方法?在上述示例中,它们是和。

    • 我在超类中有一个重载方法的基本继承情况。 下面的类扩展了上面的类: main方法只是创建一个对象(静态和动态类型)并调用: 最后打印出来 看了这个,我想既然对象的静态和动态类型都是,它会调用Person中的重载方法,该方法将作为参数。由于我显然错了,我打开了一个调试器,假设类中的getWorkDetail(this)行中对“this”的引用一定已经变成了它的超级类。然而,这不是我发现的。 显然,在

    • 本文向大家介绍Java8使用lambda表达式调用静态方法,包括了Java8使用lambda表达式调用静态方法的使用技巧和注意事项,需要的朋友参考一下 Java中的Lambda表达式允许您将功能作为参数传递给方法。您还可以使用lambda表达式调用现有的方法。 方法引用是简单的、易于阅读的lambda表达式,可以通过lambda表达式中的名称调用/引用现有的方法。可以使用方法引用引用类中定义的静态

    • 在我的项目中,我使用Guava谓词和函数来使用和筛选和转换一些集合。 在这次迁移中,我需要将guava代码更改为Java8更改。所以,我所做的改变是这样的: 到... 由于,使用方法引用的方法还允许我调试转换过程: 多亏了answer,我注意到我的Eclipse没有显示它应该显示的内容,而peek()的使用有助于显示结果。

    • 在我们的项目中,我们将迁移到Java8,并测试它的新特性。 在我的项目中,我使用Guava谓词和函数,使用和过滤和转换一些集合。 在这次迁移中,我需要更改例如guava代码到Java8的更改。所以,我正在做的改变是: 到... 使用guava调试代码非常符合要求,因为我可以调试每个转换过程,但我关心的是如何调试,例如。 由于,使用方法引用的方法还允许我调试转换过程: 由于回答,我注意到我的Ecli

    • 本文向大家介绍Java中如何调试Lambda表达式,包括了Java中如何调试Lambda表达式的使用技巧和注意事项,需要的朋友参考一下 Lambda表达式由两部分组成,一个是参数,另一个是代码或表达式。 这两个部分由箭头运算符“->”分隔。 我们可以使用NetBeans,IntelliJ和Eclipse等不同的IDE来调试Java中的lambda表达式。 始终可以创建多行lambda表达式,并使用