javastatic-typingruntime-type

Programmatically determining the compile-type of a java instance


Say I have a class that extends java.lang.Object as follows:

package pack;

public class Person {

}

And the following three instances:

Object one = new Object();
Object two = new Person();
Person three = new Person();

In order to determine the runtime-type, I just need invoke the getClass() method on the instances as follows:

System.out.println("One runtime-type : " + one.getClass());
System.out.println("Two runtime-type :  " + two.getClass());
System.out.println("Three runtime-type : " + three.getClass());

Which outputs:

One runtime-type : class java.lang.Object
Two runtime-type :  class pack.Person
Three runtime-type : class pack.Person

Now my question is how do I programmatically determine the static/compile-type of the instances above?

By static/compile-type I mean the type "on the left". It would output:

One compile-type : class java.lang.Object
Two compile-type :  class java.lang.Object
Three compile-type : class pack.Person

Solution

  • You didn't specify when you want to find out about the compile time types. From your sample output I assume you want to print out the compile-time types at runtime.

    This is not possible, (update:) unless you do it manually, knowing all types you want to use beforehand.

    If you know that you will be using only Object and Person classes, you can try the code below. You need to define a method for each of the used classes and the compiler is smart enough to use the best matching method.

    public class Program {
        static class Person {
        }
    
        public static void main(String[] params) throws Exception {
            Object one = new Object();
            Object two = new Person();
            Person three = new Person();
            System.out.println("One compile-type : " + getStaticType(one));
            System.out.println("Two compile-type : " + getStaticType(two));
            System.out.println("Three compile-type : " + getStaticType(three));
    
        }
        public static Class getStaticType(Person p) {
            return Person.class;
        }
        public static Class getStaticType(Object o) {
            return Object.class;
        }
    }
    

    This prints out:

    One compile-type : class java.lang.Object
    Two compile-type : class java.lang.Object
    Three compile-type : class Program$Person
    

    Note that this method may break if you want to apply it to interfaces, where you may run into situation when the compiler cannot decide which method to use.


    Original answer:

    You are basically asking what are the types of the variables in your source code. The only thing left from your source code at runtime is the bytecode produced by Java compiler. This bytecode doesn't include any information about the types of your variables.

    Here is what the bytecode may look like for your source code:

      public static void main(java.lang.String[]) throws java.lang.Exception;
        Code:
           0: new           #2                  // class java/lang/Object
           3: dup           
           4: invokespecial #1                  // Method java/lang/Object."<init>":()V
           7: astore_1      
           8: new           #3                  // class Program$Person
          11: dup           
          12: invokespecial #4                  // Method Program$Person."<init>":()V
          15: astore_2      
          16: new           #3                  // class Program$Person
          19: dup           
          20: invokespecial #4                  // Method Program$Person."<init>":()V
          23: astore_3      
          24: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
          27: new           #6                  // class java/lang/StringBuilder
          30: dup           
          31: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
          34: ldc           #8                  // String Two runtime-type :  
          36: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          39: aload_2       
          40: invokevirtual #10                 // Method java/lang/Object.getClass:()Ljava/lang/Class;
          43: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
          46: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
          49: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          52: return        
    }
    

    You can see that except for the invocation of Person's constructor, there is no reference to type Person, therefore the information that e.g. variable three was of type Person is lost. Also, Java doesn't have any built-in operator that you could use to capture the type of the variable at compile time.