Lambda表达式和通用接口

时间:2016-01-10 12:05:26

标签: java generics lambda

所以,我目前正在研究lambda表达式,只是遇到了一个带有泛型函数接口的例子:

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

所以,困扰我的是,在这个例子中,我们将Mapper功能接口的实例作为第二个参数传递给测试代码中的mapToInt方法。但是,在这个例子中,<T>的形式参数Mapper的价值是多少?这似乎有效,但是如果从未指定形式参数的参数,编译器如何能够解决这个问题呢?

这可能是因为抽象方法:

int map(T source);

使用T参数,并基于lambda表达式参数,将其用作T的实数类型?

此外,mapToInt中的第二个参数:Mapper<? super U> mapper,相同的问题,此示例中Mapper的类型的值是什么,<? super U>

2 个答案:

答案 0 :(得分:1)

类型U与类型T无关。

类型T允许定义功能通用接口,其单个抽象方法需要实现函数T -> int

因此,这个表达式之一将完全有效:

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

类型U允许您定义通用方法。因此,当您使用mapToInt作为第一个参数调用String[]时,编译器推断的类型U为String

因此,您需要提供一个函数String -> int,这正是name -> name.length()的作用(您可以省略lambda表达式的类型参数)。

答案 1 :(得分:0)

  

这似乎有效,但是为什么编译器能够解决这个问题   如果从未指定形式参数的参数?

这称为类型推断。确切的过程是extremely complicated,但在这种情况下,编译器能够轻松地推断出类型,因为执行指定明确地说:(String name) -> name.length()。所以你说的是真的:

  

基于lambda表达式参数,将其用作实数类型   T'

但是,在这种情况下并不是完全必要的。这也有效:

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

在这种情况下,请注意编译器知道函数mapToInt的类型参数是什么。实际上,函数使用第一个参数U[] list声明,并且由于您将String[] names作为第一个参数传递,编译器能够推断出U是什么,String[]必须可由widening reference conversion兑换为U[]。也就是说,UString或超级类String

现在,第二个参数是一个功能接口的实例,其方法接受UU的超类(Mapper<? super U>的含义)。此时,编译器基本上决定&#34;好吧,它可能只是String&#34;因为它是String不会与迄今推断的任何内容相矛盾。请注意,您实际上可以更改lambda表达式的类型:

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

现在TObject,因为您已明确指定了它。但由于T必须是UU本身的超类,因此不再清楚U究竟是什么。 U可能是String,但也可能是Object。但猜猜怎么了?这没关系!因为在编译时它没有任何效果,并且在运行时它因为类型擦除而无论如何都将是Object。但如果你真的想要,你也可以明确地澄清:

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