JEP 101: Generalized Target-Type Inference (
Java 8
的JEP
),即广义(推广)的目标类型推导。提出了2个推广加强的Case:
- Inference in argument position,在参数位置(提取形参的类型信息)的类型推导
- Inference in chained calls,链式调用上的类型推导
Java 8/9(9.0.4)/10(10.0.1)/11(11-ea)
目前都不支持 在链式调用上做类型推导。
测试分析以及结论与解决方法
为了简化代码及其类型推导的分析,选用List
,在方法上完全不涉及类型上下限,涉及方法及其签名如下:
# 与在JEP 101: Generalized Target-Type Inference中对链式调用上的类型推导所用的示例(List.nil().head()
)一致。因为Java
的List
没有head
方法,用功能和泛型参数一样的get(0)
方法替代。
// java.util.Collections#emptyList
public static final <T> List<T> emptyList();
// java.util.List#get
E get(int index);
测试代码
import java.util.Collections;
import java.util.List;
public class TypeInferenceShowcase {
public static void main(String[] args) {
// Compile OK
List<String> list1 = Collections.emptyList();
String head1 = list1.get(0);
// write as chained call.
// Compile error!!
String head = Collections.emptyList().get(0);
}
}
编译结果
Java 8/9/10/11
的编译出错信息完全一样。
## Java 8 ##
$ javac -version && echo && javac TypeInferenceShowcase.java
javac 1.8.0_162
TypeInferenceShowcase.java:12: 错误: 不兼容的类型: Object无法转换为String
String head = Collections.emptyList().get(0);
^
1 个错误
## Java 9 ##
$ javac -version && echo && javac TypeInferenceShowcase.java
javac 9.0.4
TypeInferenceShowcase.java:12: 错误: 不兼容的类型: Object无法转换为String
String head = Collections.emptyList().get(0);
^
1 个错误
# Java 10
$ javac -version && echo && javac TypeInferenceShowcase.java
javac 10.0.1
TypeInferenceShowcase.java:12: 错误: 不兼容的类型: Object无法转换为String
String head = Collections.emptyList().get(0);
^
1 个错误
# Java 11
$ javac -version && echo && javac TypeInferenceShowcase.java
javac 11-ea
TypeInferenceShowcase.java:12: 错误: 不兼容的类型: Object无法转换为String
String head = Collections.emptyList().get(0);
^
1 个错误
解决方法
因为Java 8/9/10/11
不支持 在链式调用上做类型推导,
# 即 不支持 类型信息从链路后面的调用 向 前面 传递,即 支持在调用链上类型信息的逆向传递
所以需要自己在 链式调用 上手动补上类型信息:
String head = Collections.<String>emptyList().get(0);
// ^^^^^^
实际上,在IntelliJ IDEA
中,执行变量list1
内联,生成的就是上面的代码;IntelliJ IDEA
都帮我们处理好了。具体的操作方法是:
List<String> list1 = Collections.emptyList();
// ^ 光标放list1变量上(上面或下面的都行),执行【Refactor - Inline...】(Alt+Cmd+N),见下图
String head1 = list1.get(0);
展开分析与总结梳理
JEP 101: Generalized Target-Type Inference (Java 8
的JEP
),即广义(推广)的目标类型推导。
提出了2个推广加强的Case:
- Inference in argument position,在参数位置(提取形参的类型信息)的类型推导
- 类型信息 从嵌套调用的 外面调用 向 里面的调用 传递,即 支持在嵌套调用时 类型信息的 由外往里 的逆向传递。
- 在
JDK 8
之前,只有在 赋值语句 中 会做这样类型推导(提取赋值变量的类型信息)
虽然在参数位置上,其实可以看作是 实际参数(实参)给形式参数(形参)的赋值
- Inference in chained calls,链式调用上的类型推导
- 类型信息从调用链 后面的调用 向 前面的调用 传递,即 支持链式调用时 类型信息的 由后往前 的逆向传递。
- 这个Case,可以看作是 赋值语句推广加强
因为链式只有一级调用时,就 退化成了 赋值语句 的Case。 JEP
中说的好好的,然而,上面的测试可以看到,Java 8/9/10/11
目前是不支持的 !!
总得来说,Java
的类型推导还是不够友好的,这些我们人肉解析器都能在直觉上很快完成推导觉得没问题的代码,Java
编译却是过不了,surprise~ =_=!
不过,实际业务使用和工程应用上,这并不是多大的问题,因为 需要 在调用链上 类型信息的 由后往前 的逆向传递 的情况其实很少。
说到类型推导能力的强弱,我不得不想到邻家小伙Scala
,相比起来做得就是到位贴心了。Just no surprise!
邻家小伙Scala
的类型推导能力对比
链式调用上的类型推导,对于Scala
是个小Case,毫无压力。对等的测试如下。
测试代码
class TypeInferenceScalaShowcase {
def main(args: Array[String]): Unit = {
// write as chained call.
// Compile OK
val head: String = List.empty.head
}
}
编译结果
$ scalac -version && echo && scalac TypeInferenceScalaShowcase.scala && echo 'Compile Success!'
Scala compiler version 2.12.4 -- Copyright 2002-2017, LAMP/EPFL and Lightbend, Inc.
Compile Success!
妥妥的!~ :)
相关阅读/资料
- Generic type inference not working with method chaining? - stackoverflow.com
这个SOF
的问题中也说了:Java 7/8
都不能 在 链式调用 上做类型推导。