Вызов метода для выбранного метода - PullRequest
0 голосов
/ 05 декабря 2018

У меня есть следующая программа, которая не компилируется:

Просто блок 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<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);
  }

}

Чтобы было ясно: я не ищу обходной путь, это не был бы код хорошего качества, во-первых.Мне просто любопытно, почему последний блок не компилируется.

Ответы [ 2 ]

0 голосов
/ 05 декабря 2018

Причина заключается в следующем определении в Спецификация языка 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.

Последствия этого правила могут быть продемонстрированыс другим примеры, кроме лямбда-выражений или ссылок на методы, когда они могут быть поли-выражениями:

// 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…

0 голосов
/ 05 декабря 2018

Я полагаю, что причина, по которой строка кода ниже не работает, просто из-за проблем с выводом типа.

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