主题3:目标类型
2020-01-19 06:55:43来源:博客园 阅读 ()
主题3:目标类型
目标类型就是lambda表达式实例所要被赋予的类型,通常是接口类型;
类型推导:
函数式接口的名称并不是 lambda 表达式的一部分。那么问题来了,对于给定的 lambda 表达式,它的目标类型是什么?答案是:它的类型是由其上下文推导而来。例如,下面代码中的 lambda 表达式类型是 ActionListener:
ActionListener l = (ActionEvent e) -> ui.dazzle(e.getModifiers()); |
这也意味着同样的lambda表达式在不同的上下文中,可以作为不同的类型:
Callable<String> c = () -> "done"; //Callable类型 PrivilegedAction<String> a = () -> "done"; //PrivilegedAction类型 |
编译器负责推导 lambda 表达式类型。它利用 lambda 表达式所在上下文 所期待的类型 进行推导,这个 被期待的类型 被称为 目标类型。lambda 表达式只能出现在目标类型为函数式接口的上下文中。
lambda作为目标类型的要求:
lambda 表达式对目标类型也是有要求的。编译器会检查 lambda 表达式的类型和目标类型的方法签名(method signature)是否一致。当且仅当下面所有条件均满足时,lambda 表达式才可以被赋给目标类型 T:
● T 是一个函数式接口 ● lambda 表达式的参数和 T 的方法参数在数量和类型上一一对应 ● lambda 表达式的返回值和 T 的方法返回值相兼容(Compatible) ● lambda 表达式内所抛出的异常和 T 的方法 throws 类型相兼容 |
补充:
lambda 表达式并不是第一个使用类型推导的Java 表达式:泛型方法调用和“菱形”构造器调用也通过目标类型来进行类型推导:
List<String> ls = Collections.emptyList(); List<Integer> li = Collections.emptyList(); Map<String, Integer> m1 = new HashMap<>(); Map<Integer, String> m2 = new HashMap<>(); |
目标类型上下文:
就是lambda表达式的使用场景;下面给出带有目标类型的上下文:
赋值、return:
目标类型就是被赋值或返回的类型:
Comparator<String> c; |
数组初始化器:
数组初始化器和赋值类似,只是这里的“变量”变成了数组元素,而类型是从数组类型中推导得知。
Runnable[] runnables = new Runnable[]{ |
方法和构造方法的参数:
方法参数的类型推导要相对复杂些:目标类型的确认会涉及到其它两个语言特性:重载解析(Overload resolution)和参数类型推导(Type argument inference)。
重载解析会为一个给定的方法调用(method invocation)寻找最合适的方法声明(method declaration)。由于不同的声明具有不同的签名,当 lambda 表达式作为方法参数时,重载解析就会影响到 lambda 表达式的目标类型。编译器会通过它所得知的信息来做出决定。如果 lambda 表达式具有 显式类型(参数类型被显式指定),编译器就可以直接 使用lambda 表达式的返回类型;如果lambda表达式具有 隐式类型(参数类型被推导而知),重载解析则会忽略 lambda 表达式函数体而只依赖 lambda 表达式参数的数量。
如果在解析方法声明时存在二义性(ambiguous),我们就需要利用转型(cast)或显式 lambda 表达式来提供更多的类型信息。如果 lambda 表达式的返回类型依赖于其参数的类型,那么 lambda 表达式函数体有可能可以给编译器提供额外的信息,以便其推导参数类型。
List<Person> ps = ... Stream<String> names = ps.stream().map(p -> p.getName()); |
在上面的代码中,ps 的类型是 List<Person>,所以 ps.stream() 的返回类型是 Stream<Person>。map() 方法接收一个类型为 Function<T, R> 的函数式接口,这里 T 的类型即是 Stream 元素的类型,也就是 Person,而 R 的类型就map方法的返回类型,是未知的。由于在重载解析之后 lambda 表达式的目标类型仍然未知,我们就需要推导 R 的类型:通过对 lambda 表达式函数体进行类型检查,我们发现函数体返回 String,因此 R 的类型是 String,因而 map() 返回 Stream<String>。绝大多数情况下编译器都能解析出正确的类型,但如果碰到无法解析的情况,我们则需要:
● 使用显式 lambda 表达式(为参数 p 提供显式类型)以提供额外的类型信息 ● 把 lambda 表达式转型为 Function<Person, String> ● 为泛型参数 R 提供一个实际类型。 (Stream<String> names = ps.stream().<String>map(p -> p.getName())) |
lambda 表达式函数体:
lambda 表达式本身也可以为它自己的函数体提供目标类型,也就是说 lambda 表达式可以通过外部目标类型推导出其内部的返回类型,这意味着我们可以方便的编写一个返回函数的函数:
Supplier<Runnable> c = () -> () -> { System.out.println("hi"); }; |
条件表达式(? :):
条件表达式可以把目标类型“分发”给其子表达式:
Callable<Integer> c = flag ? (() -> 23) : (() -> 42); |
转型(Cast)表达式:
转型表达式(Cast expression)可以显式提供 lambda 表达式的类型,这个特性在无法确认目标类型时非常有用:
// Object o = () -> { System.out.println("hi"); }; 这段代码是非法的 Object o = (Runnable) () -> { System.out.println("hi"); }; |
当重载的方法都拥有函数式接口作为参数时,转型可以帮助解决重载解析时出现的二义性。
原文链接:https://www.cnblogs.com/zyj-468161691/p/12213587.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- switch循环所支持的数据类型 2020-06-07
- java基本数据类型 2020-06-06
- 笑看女程序员征婚SQL,半夜巡逻民警突然对我大喊int类型占几 2020-05-31
- java方法句柄-----1.方法句柄类型、调用 2020-05-28
- Java连载118-编译一个类(包括内部函数、方法、类型、参数) 2020-05-27
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash