2017-02-01 5 views
5

Пожалуйста, рассмотрим следующий пример:Что определяет, какой функциональный интерфейс создать из лямбда?

import java.util.function.Consumer; 

public class Example { 
    public static void main(String[] args) { 
     Example example = new Example(); 

     example.setConsumer(test -> System.out.println("passed string is " + test)); //uses MyConsumer, why ? 
     example.getConsumer().accept("Test 1"); 

     example.setConsumer((MyConsumer<String>)test -> System.out.println("passed string is " + test)); //uses MyConsumer 
     example.getConsumer().accept("Test 2"); 

     example.setConsumer((Consumer<String>)test -> System.out.println("passed string is " + test)); //uses Consumer 
     example.getConsumer().accept("Test 3"); 
    } 

    private Consumer<String> consumer; 

    public Consumer<String> getConsumer() { 
     return consumer; 
    } 

    public void setConsumer(Consumer<String> consumer) { 
     this.consumer = consumer; 
    } 

    public void setConsumer(MyConsumer<String> consumer) { 
     this.consumer = consumer; 
    } 

    @FunctionalInterface 
    public interface MyConsumer<T> extends Consumer<T> { 
     @Override 
     default void accept(T value) { 
      System.out.println("In consumer string: " + value); //example thing to do 
      receive(value); 
     } 

     void receive(T value); 
    } 
} 

Что меня интересует вот первый тест. Почему он использует MyConsumer вместо Consumer? Что делать, если бы у меня было больше разных Потребителей с той же структурой лямбды, у кого есть приоритет? Кроме того, листинг, который я делаю на Test 2, отмечен как Redundant моей IDE. Это означает, что lamdba сначала создается как MyConsumer. Почему так ?

Я использую IntelliJ Idea с Javac.

+0

Я не знаю, какую IDE вы используете. Я думаю, что это JVM, использующий тип времени выполнения объекта, который «решает». – duffymo

+3

Я думаю, что Java выбирает наиболее специфический тип, который в этом случае является «MyConsumer». Если 'MyConsumer' не был подпоследователем' Consumer', я думаю, вы получили бы ошибку, заявив, что это неоднозначный звонок. – marstran

+7

Он выбирает наиболее специфический метод. Если у вас был, например, «MyConsumer2 extends Consumer » и 'setConsumer (MyConsumer2 потребитель)', тогда первый вызов был бы неоднозначным, и у вас была бы ошибка времени компиляции. См. Также [JLS 15.12.2.5] (https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5). –

ответ

6

Это в соответствии с процедурой choosing the most specific method, как это определено в спецификации языка:

Если более чем один метод член является одновременно доступным и применим к вызову метода, то необходимо выбрать один, чтобы предоставить дескриптор для отправки метода времени выполнения. Язык программирования Java использует правило, что выбран метод .

...

Функциональный тип интерфейса S является более точным, чем функционального типа интерфейса T для выражения е, если Т не является подтипом S, и один из следующих условий (где U1. .. Uk и R1 - типы параметров и тип возврата типа функции захвата S, а V1 ... Vk и R2 - типы параметров и тип возвращаемого типа функции T):

  • Если e - явно типизированное лямбда-выражение (§15.27.1), то выполняется одно из следующего:
  • R2 является недействительным.

  • R1 <: R2.

  • R1 и R2 являются функциональными типами интерфейсов, и существует хотя бы одно выражение результата, а R1 является более конкретным, чем R2 для каждого выражения результата e.

    (Результат выражение лямбда-выражения с блоком тела определяется в §15.27.2; выражении результат лямбда-выражения с выражением тела просто само тело.)

  • R1 обозначает примитивный тип, а R2 является ссылочным типом, и есть хотя бы одно выражение результата, и каждое выражение результата e является автономным выражением (§15.2) примитивного типа.

  • R1 является ссылочным типом, а R2 является примитивным типом, и существует хотя бы одно выражение результата, и каждое выражение результата e является либо автономным выражением ссылочного типа, либо поли-выражения.

  • Если е является точным методом опорного выражение (§15.13.1), а затем я) для всех г (1 ≤ ≤ к), Ui такая же, как Vi, и б) одно из следующих условий:
  • R2 является недействительным.

  • R1 <: R2.

  • R1 является примитивным типом, R2 является ссылочным типом, а объявление времени компиляции для ссылки на метод имеет тип возврата, который является примитивным типом.

  • R1 является ссылочным типом, R2 является примитивным типом, а объявление времени компиляции для ссылки на метод имеет тип возврата, который является ссылочным типом.

  • Если е выражение в скобках, то одно из этих условий применяются рекурсивно к содержащеемуся выражению.

  • Если e является условным выражением, то для каждого из второго и третьего операндов одно из этих условий применяется рекурсивно.

Поэтому MyConsumer более специфично, чем Consumer, потому что Consumer (T в спецификации) не является подтипом и оба имеют возвращаемое значение void.

Смежные вопросы