所以,我目前正在研究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>
?
答案 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[]
。也就是说,U
是String
或超级类String
。
现在,第二个参数是一个功能接口的实例,其方法接受U
或U
的超类(Mapper<? super U>
的含义)。此时,编译器基本上决定&#34;好吧,它可能只是String
&#34;因为它是String
不会与迄今推断的任何内容相矛盾。请注意,您实际上可以更改lambda表达式的类型:
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());