3

I'm a bit confused to the, I guess concept, of the Dynamic/Static types of a reference variable and Dynamic Method Resolution in Java.

Consider:

public class Types {

    @Override
    public boolean equals(Object obj){
        System.out.println("in class Types equals()");
        return false;//Shut-up compiler!
    }

    public static void main(String[] args){
        Object typ = new Types();
        typ.equals("Hi");//can do this as String is a subclass of Object
    }
}

First: the reference variable typ is of type Types, isn't it?!

So what is the reason behind typ having a Static Type Object and a Dynamic Type Types for method overriding?

Second: doesn't the compiler have enough information to call the right equals()?

If class Types does not have an overridden equals(), then it can call Object.equals() method.

In this case class Types does, and the compiler knows it.

Why can't this be a early binding like overloading? Why leave it to the JVM?

GhostCat
  • 137,827
  • 25
  • 176
  • 248

3 Answers3

5

This is an essential cornerstone of object oriented programming.

It boils down to polymorphism. See here for further reading.

You absolutely want that sub classes can override behavior; because that allows you to keep client code unchanged; but still you are able to introduce new/different behavior; simply by passing another object to that client code.

The client code knows which method to call; but the dispatch has to happen at runtime. Simply because the compiler can not know (for most cases) the exact type of an incoming parameter.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
3

The reference variable typ in your example is of type Object.

The object that it refers to is of type Types.

The compiler does not have enough information to know that the object is actually Types and use Type.equals(Object).

This may be confusing. If you write

Object typ = new Types();

Then surely the compiler knows that what there is in typ is Types. It just compiled that information into the code!

However, what if you decide to change that line into

Object type = new ObjectFactory().choose(choice).use(decision).build();

Well, now you don't actually know what the build will come up with. This will be known only at run time, after the ObjectFactory decides what to do with the values of choice and decision.

So in this case, the compiler doesn't know. The only information available to it is the static type of the variable. And it would be very bad if the compiler behaved differently when you used new than when you used a factory like above.

Now look at this scenario:

public class Types {
     public boolean equals( Object obj ) {
        // something
     }

     public boolean equals( String str ) {
        return false;
     }
}

public class Main {

    public static void main(String[] args) {
       Object typ = new Types();
       System.out.println( typ.equals("foo" ) );
    }
}

Now, in this case, Types and Main are two separate compilation units (different files etc.). Suppose the compiler decided to go according to the fact that typ is Types. It would then use Types.equals(String).

But now you can edit Types, remove the equals(String), and recompile only Types. When you run the program, you'll get a "no such method exception", because the compiler assumed Types has an equals(String) method. So your program fails, despite being a perfectly legal Java program and despite having a proper, matching method equals(Object) in the new Types.

So the real compiler uses the static type to determine whether a method exists and is legal with the given static parameters, and only at runtime, the JVM will be able to find what the actual type is, and call the applicable override of that method.

RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
0

The compiler just know the Static Type of a vriable,and use it to check some constraint in java.

Fist:

the reference variable typ is of type Types, isn't it?!

I think the compiler deem variable typ as Object,of course the typ is real point to a Types object,because of every class is subtype of Object,so it can assign to variable typ.

Second:

doesn't the compiler have enough information to call the right equals()?

No.The compiler just know program invoke the method equals() of variable typ,but it don't know the typ is point to Types instance or other Type instance.So what the real method will be invoked is not to be sure at compile-time.

Look following code:

    Object typ = new Main();
    Object o=new Object();
    typ.equals("Hi");//can do this as String is a subclass of Object
    o.equals("Hi");

and see the instruction sets in JVM:

    16: aload_1
    17: ldc           #42                 // String Hi
    19: invokevirtual #43                 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
    22: pop
    23: aload_2
    24: ldc           #42                 // String Hi
    26: invokevirtual #43                 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z

Notice the

19: invokevirtual #43 // Method java/lang/Object.equals (Ljava/lang/Object;)Z

26: invokevirtual #43 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z

The typ.equals("Hi") and o.equals("Hi") compile same instruction sets.But which method will be invoked depend on the method reference table of real instance.

#43 = Methodref          #41.#132      // java/lang/Object.equals:(Ljava/lang/Object;)Z

The #43 is a method reference which point to the real method.Every instance in jvm have a method reference table.If the class Override some method of its inherited method,the method reference will be changed to the override method.

dabaicai
  • 999
  • 8
  • 9