5

I was just wondering why does Java compiler decide whether you can call a method based on the "reference" type and not on actual "object" type? To explain I would like to quote an example:

class A {
    void methA() {
        System.out.println("Method of Class A.");
    } 
}

class B extends A {
    void methB() {
        System.out.println("Method of Class B.");
    }
    public static void main(String arg[]) {
        A ob = new B();
        ob.methB();       // Compile Time Error
    }
}

This will produce a Compile Time Error that method methB() not found in class A, although Object Reference "ob" contains an object of class B which consists of method methB(). Reason for this is that Java Compiler checks for the method in Class A (the reference type) not in Class B (the actual object type). So, I want to know whats the reason behind this. Why does Java Compiler looks for the method in Class A why not in Class B(the actual object type)?

1
  • 2
    If you passed instance ob to a method and calling methB were allowed from said method, how could it be sure all instances of A implemented a methB? Even if this were somehow possible, I doubt it would be good practice. It would be like passing A into a method, then checking its type and casting it to B in order to use the methods of B. Why not just pass B at that point?
    – Neil
    Commented Jul 31, 2013 at 8:29

6 Answers 6

8

Your example is in some way a special case. In a non trivial program you can typically not determine the class of the object a certain reference points to better than "it is of the type the reference was declared for or a subtype of it".

The whole concept of polymorphism is based on the fact that the concrete class is only known at runtime but not at compile time. This of course means that the compiler must ensure that the methods that are called on a reference will be available on the referenced object at runtime. The only methods for which this holds true are the methods of the class for which the variable was declared (including all inherited methods from the super classes).

EDIT

As @David said in his comment the reason behind this is that typing is done statically by the programmer in java. If java had type inference the typing would be done by the compiler (but still statically) and your example would be absolutely valid. But it could be invalidated by another line doing this ob = new A().

If java were a dynamic typed language the compiler wouldn't care at all (at compile time) what methods you call on an object.

9
  • 1
    With type inference, as in Scala for example, this would be possible (as long as you don't tell the compiler that what you are expecting is a A)
    – David
    Commented Jul 31, 2013 at 8:13
  • 1
    @mav3n yes exactly. Depending on the concrete program flow (different paths of if- and switch-statements) and the other classes involed at runtime the object referenced is not known. The only thing that is known about the object is that it has to follow the constraints set by the declaration of the variable (type A or subtype of A). The compiler allows only methods that all objects that fullfil this constraint will have. So only the methods A has.
    – Lesstat
    Commented Jul 31, 2013 at 9:16
  • 2
    @David you are not right with your statement about type inference because type inference would automatically declaring the variable as type B in this case (at compile time not at runtime)
    – Lesstat
    Commented Jul 31, 2013 at 9:18
  • 1
    @Lesstat you're right about type inference. What I really wanted to say, is that you answer this question from a technical point of view (difficulty to determine the class) while I think it's more the consequence of the choice of "static typing" of Java.
    – David
    Commented Jul 31, 2013 at 9:31
  • 1
    Static type checking is also done by the compiler, not by the programmer. That's the whole point of static typing.
    – Andres F.
    Commented Jul 31, 2013 at 12:18
13

By declaring ob as a A, you're telling the compiler that you are using an object that behaves like A.

Java performs strict type checking. If you tell that ob is an instance of A, then, whatever you assign to it, Java will make sure you keep to A's contract.

This can help you create loosely coupled code that resists to future changes.

Let's say at some point you need another implementation of A :

class C extends A {
    void methA() {
        System.out.println("Overriden methA.");
    }
}

And that you have sticked to A contract :

   public static void main(String arg[])
   {
      A ob = new B();
      ob.methA();  // output: "Method of Class A."
   }

Then, you can easily substitute C to B, because your code does not depend on B :

   public static void main(String arg[])
   {
      A ob = new C();
      ob.methA(); // output: "Overriden methA."
   }

Java lets you choose the level of abstraction you need and then help you enforce it.

If you write code that needs to call methB (and so depends on B class), then tell it to Java:

   public static void main(String arg[])
   {
      B ob = new B();
      ob.methB();  // output: "Method of Class B."
   }

Now only instances of B or subclasses of B can be assigned to ob.

2
  • @mav3n Not really sure why this isn't the accepted answer.
    – Steve P.
    Commented Aug 3, 2013 at 21:36
  • @SteveP. I couldn't accept two answers, so I accepted the one which I felt is logically more apt answer to my question. I am not saying that David's answer is not apt, its apt too, its just that maybe I couldn't catch the point he wanted me to catch. Commented Aug 27, 2013 at 7:08
4

Let me propose a question. Lets have this code:

class A {
    void methA() {
        System.out.println("Method of Class A.");
    }

    public static void main(String arg[]) {
        A ob;
        if ((new Random().nextInt(2)) == 0){
            ob = new B();
        } else {
            ob = new C();
        }
        ob.methB(); // what now?
    } 
}

class B extends A {
    void methB() {
        System.out.println("Method of Class B.");
    }
}

class C extends A {
    void methC() {
        System.out.println("Method of Class C.");
    }
}

Now, what will happen if instance of class C is saved in ob? What method will be called?

The point is, that method calls are resolved at compile time and compiler cannot know what concrete class instance will be inside variable during compilation. That is why you can only call method that exist in class that is defined as type of given variable. And if said method is virtual, then the runtime will pick correct overload when the program runs. This is basis for late binding in OOP languages.

1
  • The example could be made more interesting by having classB and classC define methods called methodX but different return types. Even if a compiler were willing to allow invocation of methodX on a variable of type A on the optimistic assumption that the instance would identify an object having such a type, it would have no way of knowing what kind of data the function call would return.
    – supercat
    Commented Apr 25, 2017 at 18:38
3

I was just wondering why does Java compiler decide whether you can call a method based on the "reference" type and not on actual "object" type?

Instead of debating on Why does Java compiler decide whether you can call a method based on the “reference” type and not on actual “object” type? let say java compiler does allow this.Consider the following example

class A {
    void methA() {
        System.out.println("Method of Class A.");
    } 
}

class B extends A {
    void methB() {
        System.out.println("Method of Class B.");
    }
    public static void main(String arg[]) {
        A ob = new B();
        ob.methC();       // Compile Time Error
    }
}

See the method ob.methC(); ? This method is neither in Class A or Class B. If Java compiler would have allowed this it would have cause an Error. As the saying goes it is better to take care of your self than to fall ill and eat medicines. Java was designed to be simple and not allow error that we know have high probability of occurring(One of the reason why Exceptions were introduced from very beginning).

Also whole concept of polymorphism is based on the fact that the concrete class is only known at runtime but not at compile time. So for safety java compilers are designed that way. Though you can decide just to declare your method in superClass(like making it abstract) and then implement it in the sub class.

1

My favorite analogy for polymorphism is to use teachers. This will provide more clarity rather than the abstract MethodA() and MethodB(). Say you have a Teacher and a Math teacher. The latter is a more specific version and represents an IS-A relationsihp. A Math Teacher IS a teacher, so it can do anything a teacher can do, but the opposite is not true. Not all teachers can teach math. If I have a teacher reference,

Teacher teacher = GetTeacher();
teacher.Teach();

It would not be safe to assume the teacher knows math, it could be a lanuage or science teacher. We do know that all teachers have the Teach() method, so this is safe to call. The reference defines the interface or contract. This separation of interface from the implementation is a very powerful OOP principal. For this reason it is often encouraged to decouple your implementation entirely. I don't need to know what kind of teacher the reference holds, because I know it can teach regardless.

This can continue even further as you get more specific, you can have a Calculus teacher which IS a math teacher. Both math teacher and calculus teacher have the method DoEquation() and Teach(), but only the calculus teacher can call DoAdvancedEqation().

Thinking about inheritance as an IS-A relationship should help you understand why it is not safe to make assumptions such as proposed in the question.

0

Your object is type of 'A' and 'A' is not having a definition of 'methB()'. That is why Java compiler is not allowing.

class ClassA
{
    void MethodA() { }
}

class ClassB extends ClassA
{
    void MethodB() { }
}

class Program
{
    public static void main(string[] args)
    {
        ClassA a = new ClassB();
        a.MethodB(); // Compile time error.
    }
}

If you override any method in the derive class then at runtime it will call the actual method depending on the object which is pointed by the reference.

class ClassA
{
    void MethodA() 
    { 
        Console.WriteLine("A"); 
    }
}

class ClassB extends ClassA
{
    void MethodA() 
    { 
        Console.WriteLine("B"); 
    }
}

class Program
{
    public static void main(string[] args)
    {
        ClassA a = new ClassB();
        a.MethodA(); // Output: B
    }
}
3
  • 3
    Please read the question carefully before answering. I think you didn't get what I asked. Commented Jul 31, 2013 at 8:48
  • @mav3n no, I think you don't understand his answer, no surprise given the question... Commented Jul 31, 2013 at 12:27
  • @DaveHillier: I think you also need to read the question again. I already mentioned the point given by DeveloperArnab in my question that it would give compile time error as Java compiler check for the method in Class A and he doesn't finds the method methB() in it, so it gives error. My question was why does java compiler looks for the method in Class A and not in Class B? Correct Answer to this has been given by Lesstat, David and Aniket Thakur Commented Jul 31, 2013 at 14:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.