使java编译器输出类型推断信息

时间:2014-06-18 11:10:13

标签: java generics javac type-inference

是否可以让javac输出有关它为方法调用推断的类型的信息?

例如,我想知道T调用中正式类型bar的推断内容。

private static <T> void bar() { ... }
public void foo() {
  bar();
}

我正在探索javac -Xprint和朋友,但无法找到任何能够揭示这一详细程度的内容。


编辑示例。我原本不打算把它放在原因,因为它会使答案复杂化。我主要想从javac中获取调试信息。无论如何,这是一个激励的例子:

public class Scratch {
  private static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
    throw (T) t; // Warning: Type safety: Unchecked cast from Throwable to T
  }
  public void foo() {
    sneakyThrow(new Exception());
  }
}

这会编译,但对T的实际类型的任何合理决定都应该产生Throwable,并且需要foo() throws Throwable。 Eclipse似乎认为它是RuntimeException。我想知道javac认为发生了什么。如果javac处理throws条款中的类型参数是一个错误,那么这个问题的答案将允许我证明它。

2 个答案:

答案 0 :(得分:2)

可以详细查看javac推断/解决的内容等。为此,您需要使用隐藏/不支持/未记录的选项:-XDverboseResolution。如果想要查看所有信息,那么要传递的值是“all”,如:-XDverboseResolution = all。如果只想查看泛型方法的实例化,则选项为:-XDverboseResolution = deferred-inference。对于原始问题中的代码,我得到以下输出:

命令:javac -XDverboseResolution=deferred-inference Scratch.java

输出:

Scratch.java:6: Note: Deferred instantiation of method <T>sneakyThrow(Throwable)
    sneakyThrow(new Exception());
               ^
  instantiated signature: (Throwable)void
  target-type: <none>
  where T is a type-variable:
    T extends Throwable declared in method <T>sneakyThrow(Throwable)
Note: Scratch.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

从这个输出中你可以推断T已经被实例化为Throwable。

我希望这就是你要找的东西。

答案 1 :(得分:0)

您要找的是 type erasure ,而不是类型推断

我不知道javac可以输出类型擦除详细信息的任何选项。

本质上,编译器将所有类型参数擦除到从类型参数派生的最低已知类型:

  • <T>变为Object
  • <T extends Number>变为Number
  • <T extends Comparable<T>>变为Comparable
  • <T extends Cloneable & Comparable<T>>变为Cloneable
  • <T extends Object & Comparable<T>>变为Object
  • <S, T extends S>变为Object,Object

您可以随时通过调用.class来查看javap -c <class_name>.class文件,以查看生成的字节代码是什么。

* N.B。请记住,编译器有各种开关,允许您保留调试信息。请参阅javac -g选项。

因此,在下面的实验中,您的代码会稍微重做(注意类声明中的类型参数和sneakyThrow(T t) throws T

public class Scratch <T extends Throwable> {
  private static <T extends Throwable> void sneakyThrow(T t) throws T {
    throw (T) t;
  }
  public void foo() throws T {
    sneakyThrow((T) new Exception());
  }
}

...以下是使用javap -v -c Scratch.class进行编译后javac -g:none Scratch.java输出的一部分:

  Constant pool:
  #1 = Methodref          #6.#19     //  java/lang/Object."<init>":()V
  #2 = Class              #20        //  java/lang/Exception
  #3 = Methodref          #2.#19     //  java/lang/Exception."<init>":()V
  #4 = Methodref          #5.#21     //  Scratch.sneakyThrow:(Ljava/lang/Throwable;)V
  #5 = Class              #22        //  Scratch
  #6 = Class              #23        //  java/lang/Object
  #7 = Utf8               <init>
  #8 = Utf8               ()V
  #9 = Utf8               Code
  #10 = Utf8               sneakyThrow
  #11 = Utf8               (Ljava/lang/Throwable;)V
  #12 = Utf8               Exceptions
  #13 = Class              #24        //  java/lang/Throwable
  #14 = Utf8               Signature
  #15 = Utf8               <T:Ljava/lang/Throwable;>(TT;)V^TT;
  #16 = Utf8               foo
  #17 = Utf8               ()V^TT;
  #18 = Utf8               <T:Ljava/lang/Throwable;>Ljava/lang/Object;
  #19 = NameAndType        #7:#8      //  "<init>":()V
  #20 = Utf8               java/lang/Exception
  #21 = NameAndType        #10:#11    //  sneakyThrow:(Ljava/lang/Throwable;)V
  #22 = Utf8               Scratch
  #23 = Utf8               java/lang/Object
  #24 = Utf8               java/lang/Throwable

  public Scratch();
    Code:
       0: aload_0       
       1: invokespecial #1      // Method java/lang/Object."<init>":()V
       4: return        

  public void foo() throws T;
    Code:
       0: new           #2      // class java/lang/Exception
       3: dup           
       4: invokespecial #3      // Method java/lang/Exception."<init>":()V
       7: invokestatic  #4      // Method sneakyThrow:(Ljava/lang/Throwable;)V
      10: return        
}

正如您所看到的,Throwable类型删除所确定的内容。