-1

When running this part of the code, why do I get 'O' instead of 'P'?

class Pop{
        @Override
        public boolean equals(Object o){
            System.out.print("O");
            return false;
        }
        public boolean equals(Pop p){
            System.out.print("P");
            return false;
        }
        public static void main (String[] args) 
        {
            Pop p1 = new Pop();
            Object o = p1;
            o.equals(p1);
        }
    }
Caspar
  • 21
  • 1
  • 1
    Because over*load* resolution -- choosing among methods based on argument types -- is performed at compile time, based on the declared types of the expressions designating the target object and method arguments. Only over*ride* resolution -- choosing among methods of a given signature based on the type of the object on which it is invoked -- happens dynamically at runtime. – John Bollinger Dec 18 '16 at 06:15
  • Jashua Bloch in his book _Effective Java_, deals with exactly this situation. – Mordechai Dec 18 '16 at 06:18
  • [You can find the explanation in the answer below ](http://stackoverflow.com/questions/30109231/method-overload-resolution-in-java) – Bkmm3 Dec 18 '16 at 06:31
  • 1
    Because you didn't research what `override` is and *when* it happens? – Tom Dec 18 '16 at 06:31
  • @JohnBollinger Do you mean the _equals_ method that get a Pop as the parameter is actually an _overload_, not an _override_? – Caspar Dec 18 '16 at 07:05
  • Yes, @Caspar. `Pop.equals(Pop)` has the same name as another method belonging to class `Pop`, but different parameters. That is an overload. Contrast with `Pop.equals(Object)`, which has the same name and parameter list as an inherited method; this is an override of the inherited method. Note, too, that even if `Pop` did not override `equals(Object)`, `Pop.equals(Pop)` would still be an overload of the version inherited from `Object`. – John Bollinger Dec 18 '16 at 07:13
  • @JohnBollinger All right, I fully understand. Thank you. – Caspar Dec 18 '16 at 07:29

1 Answers1

3

Here's what happens when you code o.equals(p1):

  1. When Java compiles this, it looks at the type of o, and it sees that it is Object, because that's what you declared it to be. It won't know that it's a Pop. As far as the compiler is concerned, it could be an object of any type. (You can tell by looking at the previous code that it will be a Pop, but the compiler doesn't do that kind of analysis.)

  2. The Java compiler looks at the Object class for a matching definition of equals. There's only one definition of equals, and it matches (that is, the argument list is compatible with the definition). So the Java bytecode is created to say, "We're calling the method equals defined in Object."

  3. At run time, when the call is made, it calls the method equals defined in Object, like the bytecode says. Any time Java makes a call, though, it will use an overriding call if there is one. So it looks at the actual class, Pop, and sees whether Pop (or any other superclass between Object and Pop, but there isn't one here) overrides that method in Object. The first method in Pop does override it, so that's the one that's executed, and "O" gets printed. The second equals method isn't looked at at all, since it doesn't override the equals in Object.

I don't know what mechanism the compiler uses for storing override information. But in another compiler for a different language that I've worked with, methods get indexes, or "slot" numbers, in a vector. When Object is defined, its methods get unique slots; the slot for equals might be, say, 7. So the 7th entry in the vector would be the address of equals. When Pop is defined, since the first equals overrides the one in Object, it would also get slot 7. The second equals doesn't override anything, so it gets a new slot number, maybe 18. So there would be a vector where index 7 contains the address of the first equals in Pop, and index 18 contains the address of the second one. Now, when the code calls equals in Object, the code says something like "call whatever method is in slot 7". For a Pop object, the method in slot 7 will be the first equals, which prints "O". But it never looks at slot 18. I doubt that Java operates exactly in this way, but the effect is similar. Basically, there's some ID that gets looked up, and the method for that ID is called; Java does not, at run time, search for another method defined in the class that might get called.

MORE: If you really want the behavior where it prints "P" if its argument is a Pop, you can do it like this:

    @Override
    public boolean equals(Object o){
        if (o instanceof Pop) {
            return equals((Pop)o);  
            // this equals will be the one below, because of overload resolution
        }
        System.out.print("O");
        return false;
    }
ajb
  • 31,309
  • 3
  • 58
  • 84