在所选方法参考内联上调用方法

时间:2018-12-04 23:05:20

标签: java language-lawyer method-reference

我有以下程序无法编译:

第1块可以正常编译并按预期工作-我可以有条件地选择一个对象并在其上内联调用一个方法。

第2块也可以正常编译并按预期工作-我可以有条件地将方法引用分配给Supplier<String>变量,然后对该变量调用.get()

但是块3无法编译:

Lambda.java:31: error: method reference not expected here
    String res = ((Supplier<String>) (args.length > 0 ? Lambda::foo : Lambda::bar)).get();
                                                        ^
Lambda.java:31: error: method reference not expected here
    String res = ((Supplier<String>) (args.length > 0 ? Lambda::foo : Lambda::bar)).get();

我认为结合方框1和2中的思想,由于((Supplier<String>) (args.length > 0 ? Lambda::foo : Lambda::bar))的类型为Supplier<String>,我将能够执行方框3。

import java.util.function.Supplier;

class Lambda {

  private final String s;

  private Lambda(String s) {
    this.s = s;
  }

  private static String foo() {
    return "foo";
  }

  private static String bar() {
    return "bar";
  }

  private String str() {
    return s;
  }

  public static void main(String... args) {
    // Block 1
    Lambda l1 = new Lambda("x");
    Lambda l2 = new Lambda("y");
    System.out.println((args.length > 0 ? l1 : l2).str());

    // Block 2
    Supplier<String> s = (args.length > 0 ? Lambda::foo : Lambda::bar);
    System.out.println(s.get());

    // Block 3
    String res = ((Supplier<String>) (args.length > 0 ? Lambda::foo : Lambda::bar)).get();
    System.out.println(res);
  }

}

要清楚:我不是在这里寻找解决方法,首先这不是高质量的代码。我很好奇为什么最后一个块无法编译。

2 个答案:

答案 0 :(得分:9)

原因是The Java® Language Specification, §15.25.3

中的以下定义
  

15.25.3。参考条件表达式

     

如果引用条件表达式出现在分配上下文或调用上下文(§5.2§5.3)中,则它是一个多边形表达式。否则,它是一个独立的表达式。

由于casting contexts不在列表中,因此引用条件表达式是该上下文中的独立表达式,这意味着其结果类型仅由其参数类型确定。由于方法引用没有单独的类型,而是依赖于目标类型,因此不能在此处使用它们(没有提供构造的其他类型)。

§15.13进行比较:

  

方法引用表达式始终是多边形表达式(§15.2)。

     

如果方法引用表达式在程序中的某个位置而不是赋值上下文(§5.2),调用上下文(§5.3)或变量中出现,则是编译时错误。投射上下文(§5.5)。

因此,通常情况下,强制转换上下文是方法引用的有效位置,但由于强制转换上下文中条件表达式的独立表达性质,强制转换上下文和条件表达式的组合实际上是无效的。

除非我们在表达式中提供显式类型,例如
args.length > 0 ? (Supplier<String>)Lambda::foo : (Supplier<String>)Lambda::bar,当然。

当lambda表达式或方法引用可以是poly表达式时,也可以用其他示例来证明此规则的后果:

// poly expression, infers List<Number> for Arrays.asList(0) and 0 is assignable to Number
List<Number> list = args.length>0? Arrays.asList(0): null;

// stand-alone expression, fails with "List<Integer> cannot be converted to List<Number>"
List<Number> list = (List<Number>)(args.length>0? Arrays.asList(0): null);

我不知道为什么强制转换上下文不符合将引用条件表达式转换为多边形表达式的要求,但这就是Java 8到Java 11的指定方式……

答案 1 :(得分:0)

我相信下面的代码行不起作用的原因仅仅是由于类型推断问题。

String res = ((Supplier<String>) (args.length > 0 ? Lambda::foo : Lambda::bar)).get();

,因此可以通过如下显式强制转换来解决:

String res = (args.length > 0 ? (Supplier<String>)Lambda::foo : (Supplier<String>)Lambda::bar).get();