2016-01-10 5 views
0

Итак, я в настоящее время изучает лямбда-выражения и просто наткнулся на пример с родовыми функциональными intefaces:лямбда-выражения и общий интерфейс

@FunctionalInterface 
public interface Mapper<T> { 
    int map(T source); 

    public static <U> int[] mapToInt(U[] list, Mapper<? super U> mapper) { 
     int[] mappedValues = new int[list.length]; 

     for(int i = 0; i< list.length; i++) { 
      mappedValues[i] = mapper.map(list[i]); 
     } 

     return mappedValues; 
    } 
} 

и тестовый код

public static void main(String[] args) { 
     System.out.println("Mapping names to their lengths:"); 
     String[] names = {"David", "Li", "Doug"}; 
     int[] lengthMapping = Mapper.mapToInt(names, (String name) -> name.length()); 
     printMapping(names, lengthMapping); 

    } 

    public static void printMapping(Object[] from, int[] to) { 
     for(int i = 0; i < from.length; i++) { 
      System.out.println(from[i] + " mapped to " + to[i]); 
     } 
    } 

Итак, что беспокоит me, в этом примере мы передаем экземпляр функционального интерфейса Mapper в качестве второго аргумента в методе mapToInt в тестовом коде. Но, каково было бы значение формального параметра <T> из Mapper, в этом примере? Кажется, что это работает, но почему компилятор может понять это, если не задал аргумент для формального параметра?

Это возможно потому, что абстрактный метод:

int map(T source); 

использует T параметра, и на основе выражения аргумента лямбды, использует это в качестве вещественного типа для T?

Также, второй параметр в mapToInt: Mapper<? super U> mapper, тот же вопрос, каково было бы значение в этом примере для типа Mapper, <? super U>?

ответ

1

типа U не имеет отношения к типу T.

Т-типа допускает, чтобы определить функциональную общий интерфейс, где его единственный абстрактный метод требует, чтобы реализовать функцию T -> int.

Так один из этого выражения будет вполне допустим:

Mapper<Boolean> mapper = b -> b.hashCode(); //a function Boolean -> int 

типа U позволяет вам определить общий метод. Поэтому, когда вы вызываете mapToInt с String[] в качестве первого параметра, тип U, выведенный компилятором, равен String.

Таким образом, вы должны предоставить функцию String -> int, что и делает name -> name.length() (вы можете опустить параметр типа выражения лямбда).

0

Это, похоже, сработает, но почему компилятор может понять это из , если не указан аргумент для формального параметра?

Это называется вывода типа. Точная процедура - extremely complicated, но в этом случае компилятор может легко вывести тип, потому что вы do укажите его явно: (String name) -> name.length(). Итак, вы говорите правду:

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

Однако в данном случае это не обязательно.Это тоже будет работать:

int[] lengthMapping = Mapper.mapToInt(names, name -> name.length()); 

В этом случае заметит, что компилятор знает, что параметр типа функции mapToInt есть. Действительно, функция, объявленная с первым параметром U[] list, и поскольку вы передаете String[] names в качестве первого параметра, компилятор может сделать вывод, что все, что должно быть U, String[] должно быть конвертировано в U[] по номеру widening reference conversion. То есть, U - String или суперкласс из String.

Теперь второй параметр представляет собой экземпляр функционального интерфейса, метод которого принимает то, что либо U, либо суперкласс из U (это то, что означает Mapper<? super U>). В настоящий момент компилятор в основном решает: «Ну, может быть, это просто String», потому что это String не будет противоречить ничему выведенному до сих пор. Обратите внимание, что вы можете изменить тип лямбда-выражения:

int[] lengthMapping = Mapper.mapToInt(names, (Object name) -> name.hashCode()); 

Теперь T будет Object, потому что вы указали его в явном виде. Но так как T должен быть суперклассом U или U сам не совсем ясно, что именно такое U. U может быть String, но это также Object. Но угадайте, что? Это не имеет значения! Потому что во время компиляции это не имеет никакого эффекта, и во время выполнения это будет Object в любом случае из-за стирания типа. Но если вы действительно этого хотите, вы можете уточнить это явно:

int[] lengthMapping = Mapper.<Object>mapToInt(names, name -> name.hashCode()); 
Смежные вопросы