所选方法引用的内联调用方法

iob*_*der 9 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();
Run Code Online (Sandbox Code Playgroud)

我认为该想法在块1组合和2 I将能够为一体的类型执行块3 ((Supplier<String>) (args.length > 0 ? Lambda::foo : Lambda::bar))Supplier<String>.

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);
  }

}
Run Code Online (Sandbox Code Playgroud)

需要说明的是:我不是在寻找一种解决方法,这首先不是高质量的代码.我只是好奇为什么最后一个块无法编译.

Hol*_*ger 9

原因在于Java®语言规范,§15.25.3中的以下定义

15.25.3.参考条件表达式

参考条件表达式是聚表达式如果它出现在赋值上下文或调用上下文(§5.2.§5.3).否则,它是一个独立的表达式.

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

§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);
Run Code Online (Sandbox Code Playgroud)

我不知道为什么转换上下文不符合引用条件表达式作为多表达式的条件,但这就是它如何为Java 8指定为Java 11 ...