【问题标题】:Call method on chosen method reference inline在选定的方法参考内联调用方法
【发布时间】:2019-05-06 10:59:57
【问题描述】:

我有以下程序无法编译:

只需块 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 中的想法我将能够执行块 3,因为 ((Supplier&lt;String&gt;) (args.length &gt; 0 ? Lambda::foo : Lambda::bar)) 的类型是 Supplier&lt;String&gt;

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

}

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

【问题讨论】:

  • Supplier&lt;String&gt; s = (args.length &gt; 0 ? Lambda::foo : Lambda::bar); 这样的 lambda 表达式真的让我感到惊讶,你能指出我以不同方式编写 lambda 表达式的任何文档页面
  • @Deadpool 此示例中没有 lambda 表达式。 Lambda::fooLambda::bar 是方法引用,它们没有什么特别之处。 args.length &gt; 0? expression1: expression2 形式的表达式就是三元运算符。

标签: java language-lawyer method-reference


【解决方案1】:

原因是The Java® Language Specification, §15.25.3中的如下定义

15.25.3。引用条件表达式

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

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

§15.13比较:

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

如果方法引用表达式出现在程序中除赋值上下文 (§5.2)、调用上下文 (§5.3) 或强制转换上下文 (@ 987654329@)。

因此,虽然转换上下文通常是方法引用的有效位置,但由于转换上下文中条件的独立表达式性质,转换上下文和条件表达式的组合结果证明是无效的。

除非我们在表达式中提供显式类型,例如 with
args.length &gt; 0 ? (Supplier&lt;String&gt;)Lambda::foo : (Supplier&lt;String&gt;)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);

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 但是((Supplier&lt;String&gt;) (args.length &gt; 0 ? Lambda::foo : Lambda::bar)).get()的类型不应该是Supplier&lt;String&gt;吗?我不明白为什么我不能在上面调用.get()(以及为什么编译器错误消息指向那个特定位置)
    猜你喜欢
    • 1970-01-01
    • 2017-11-09
    • 2013-04-14
    • 2012-07-05
    • 1970-01-01
    • 2018-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多