简体   繁体   中英

Make java compiler output type inference information

Is it possible to get javac to output information about the types it's inferring for method invocations?

For example, I want to know what is inferred for the formal type T in the invocation of bar .

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

I was exploring javac -Xprint and friends, but can't find anything that exposes this level of detail.


EDIT Example. I didn't want to put this up originally because it'll complicate answers. I'm primarily interested in getting debug info out of javac . Anyway, this was the motivating example:

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());
  }
}

This compiles, but any reasonable decision as to the actual type of T should yield Throwable , and require that foo() throws Throwable . Eclipse seems to think it's RuntimeException . I want to know what javac thinks is happening. If it's a bug in javac 's processing of type parameters in the throws clause, the answer to this question would allow me to prove it.

It's possible to see with a lot of detail what javac has inferred / resolved etc. To do that you need to use the hidden / unsupported / undocumented option: -XDverboseResolution. If one want to see all the information then the value to pass is 'all' as in: -XDverboseResolution=all. If one only want to see the instantiation of the generic methods then the option is: -XDverboseResolution=deferred-inference. For the code in the original question I get the following output:

command: javac -XDverboseResolution=deferred-inference Scratch.java

output:

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.

From this output you can infer that T has been instantiated to Throwable.

I hope this is what you were looking for.

What are you looking for is type erasure as opposed to type inference .

I don't know of any option to javac which would output type erasure details.

Essentially compiler erases all type parameters to the lowest known type derived from the type parameter:

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

You can always inspect your .class file by calling javap -c <class_name>.class to see, what is the resulting byte code.

*NB Please keep in mind that compiler has various switches allowing you to preserve debug info. See javac -g option.

So in terms of experiment below is your code slightly reworked (note type parameter in class declaration and 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());
  }
}

... and below is part of the output of javap -v -c Scratch.class after compiling it using 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        
}

As you can see Throwable is what type erasure settled on.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM