然后是 desugar,看起来是不是挺像解除语法糖的。这个过程会对类进行变换。把 get() 的返回类型由 char[] 变为 Object 的过程,就在这个函数中。最终是由 com.sun.tools.javac.tree.TreeTranslator#translate(T) 这个函数完成。
其中函数 get 会被变换:
public static <T> T get() { return (T) "x"; }会变成
public static Object get() { return (Object) "x"; }同时,String.valueOf(get()) 中的 get() 方法,其类型 char[] 也会变成 Object。这个过程在 com.sun.tools.javac.comp.TransTypes#retype 中完成。
// tree: get(); erasedType: java.lang.Object; target: char[] JCExpression retype(JCExpression tree, Type erasedType, Type target) { .. }由于 get() 返回类型为 T,所以这个函数会把 T 换成 Object,同时插入一条指令,将 Object 转换成 char[]。
retype 的本意如下面这个例子所示:
class Cell<A> { A value; } Cell<Integer> cell; Integer x = cell.value;此时,会将 cell.value 返回值设置成 Object, 并且插入强制类型转换的指令。
但是在我们这次分析的情况中,get() 的返回类型 T 已经被推断出是 char[],为什么又因为其定义是 T,然后被擦除,变为 Object 呢?这样一来,String.valueOf 选择了 String.valueOf(char[]),而 get() 的类型是 Object,又要强制转化成 char[]。既然如此,为什么不选择 String.valueOf(Object) 呢?感觉像个 bug 啊。我现在也不能理解为什么要这么做。
最后是 generate,生成字节码。生成 get() 字节码的源代码位于 com.sun.tools.javac.jvm.Gen#genExpr,此时参数 tree 为 get(),pt 为 char[]。生成 get() 字节码时,其返回类型已为 Object,而非 char[]。
最后总结一下文中最开始时提到的两个问题:
对 get() 返回类型的推断
对 String.valueOf 这个重载方法的选择
在 JDK6 中
get() 返回类型直接设置为 Object
String.valueOf 选择了 String.valueOf(Object)
String.valueOf 与 get() 匹配
在 JDK8 中
get() 返���类型先设置为 DeferredAttr.DeferredType
遍历 String.valueOf 的方法,在满足条件的 String.valueOf(Object) 和 String.valueOf(char[]) 中选择更为具体的 String.valueOf(char[]),get() 的返回类型也为 char[]
get() 的返回类型在 desugar 阶段被擦除,设置为 Object,同时强制转为 char[]