Stranger
Stranger

Reputation: 862

Static vs Dynamic Binding Logic

I have the following code:

import java.lang.*;

public class Program
{
    public static void main(String [] args) 
    { 
        B a = new A(); 

        a.p(10);  
        a.p(10.0); 
    } 
} 
    class B { 
        public void p(double i) 
        { 
            System.out.println(i*2); 
        } 
    }

    class A  extends B{ 
        public void p(int i) 
        { 
            System.out.println(i); 
        } 
    } 

When I execute this code using B a = new A() , I get 20.0 in both cases which makes sense because overloading is handles during compile time where the compiler looks at the declared type and calls a function appropriately. Since our declared type was class B, class B's method was called in both cases. Now if I do A a = new A(); , I should be getting 10 in both answers but I am not. I am getting 10 for a.p(10) and 20.0 for a.p(10.0). Based on the concept of static binding and whole notion of overloading being done by static binding which looks at the declared type as opposed to the actual type, why is the result coming out this way ? I would very much appreciate your help.

Upvotes: 3

Views: 766

Answers (7)

KNU
KNU

Reputation: 2515

To make the effect of widening of int to double more vivid I have created another example which is worth looking. Here, instead of double I have created a class called Parent and instead of int a Child class is created.

Thus,
double ~ Parent
int~Child

Obviously child object can be widened to Parent reference.

package test;
public class OOPs {
    public static void main(String[] args) {
        Child ch = new Child(); // like int 10
        Parent pa = new Parent();// like double 10.0

        B a = new A(); // case 2 : A a = new A();

        a.p(ch);// 10
        a.p(pa);// 10.0
    }
}

class B {
    public void p(Parent i) {
        System.out.println("print like 20");
        System.out.println(i.getClass().getName());
    }
}

class A extends B {
    public void p(Child i) {
        System.out.println("print like 10");
        System.out.println(i.getClass().getName());
    }
}

class Parent {
    String name;

    Parent() {
        name = "Parent";
    }

    public String getName() {
        return name;
    }
}

class Child extends Parent {
    String name;

    Child() {
        name = "Child";
    }

    public String getName() {
        return name;
    }
}

Case 1 - Output (B a = new A();)
print like 20
test.Child
print like 20
test.Parent

Case 2 - Output (A a = new A();)
print like 10
test.Child
print like 20
test.Parent

Upvotes: 0

Raffaele
Raffaele

Reputation: 20885

When the object has declared type B, the double version is invoked because it's compatible with an int argument, since in Java int is a subtype of double.

When the object is declared as a A, it has the method p() overloaded with two versions:

p(int arg);
p(double arg);

So, when you pass an int, the first version is picked because it's more accurate, and when you pass double the second one, because it's the most specific signature.

For reference, see the relevant JLS at §15.12.2 and this post by Gilad Bracha. BTW, don't try to figure out how the language should behave based on what you think is the most logical way, because every programming language is an engineering effort, and this means that there's a price you pay for whatever you take. The primary source of information for Java are the JLS, and if you read it carefully, you'll (surprisingly?) discover that there are even cases where the line in the source code is ambiguous and cannot be compiled.

Upvotes: 1

Renjith
Renjith

Reputation: 3274

In your case, your are doing overloading which will get binded at compile time(static binding.).And static binding happens with type of reference rather than the type of object the reference is pointing. In your first case you are using a reference variable of B and assigning an object of A to it.Since your reference is B, the method p(double) from B will get binded statically even if you use an int(since int can be widened to double).

In the second case you are using reference as A itself.In this case, you have two p() methods available.One is p(double) from B and other p(int) from A.So p(10) will call p(int) and p(10.0) will call p(double)

Try this:

class B { 
    public void p(String i) 
    { 
        System.out.println("parent:"+i); 
    } 
}

class A  extends B{ 
    public void p(int i) 
    { 
        System.out.println(i); 
    } 
} 
public class Test1 {
    public static void main(String args[]) {

         A a = new A(); //arg
            a.p(10);  
            a.p("sample"); 
    }
}

If you change the line marked arg to B a = new A(), you will see compiler trying to call parent p in both the cases.

Upvotes: 1

paulsm4
paulsm4

Reputation: 121649

This counter-example might help:

import java.lang.*;

public class X
{
    public static void main(String [] args) 
    { 
        B c = new A(); 

        c.p(10);  
        c.p(10.0); 
        c.p("AAA");
        ((A)c).p(10);
    } 
} 
    class B { 
        public void p(String s) 
        { 
            System.out.println("B: my string is " + s); 
        } 

       public void p(double i) 
        { 
            System.out.println("B: twice my double is: " + i*2); 
        } 
    }

    class A  extends B{ 
        public void p(int i) 
        { 
            System.out.println("A: my number is " + i); 
        } 
    } 

Output:

C:\temp>java X
B: twice my double is: 20.0
B: twice my double is: 20.0
B: my string is AAA
A: my number is 10

The issue is:

1) You're declaring the type as "B" (not "A")

2) B.p(10) can accept an int as a floating point argument

3) Consequently, that's what you're getting

It's really an issue of what argument types can be implicitly converted, than what methods are overloaded or overridden.

Upvotes: 1

Achrome
Achrome

Reputation: 7821

When you write A a = new A() you create a new object of type A, which will have 2 methods. A.p(int) and B.p(double), and when you call A.p(10.0), it will call B.p(double) due to lack of conversion.

Upvotes: 1

PermGenError
PermGenError

Reputation: 46408

Its because your method p is not an overridden method, it is just inhereted in your sub-class when you use

Super sup = new Sub();
sup.p(int);
sup.p(double);

In this case as your Super class has a method which takes double as a parameter and aa an int can fit into a double your Super-class's method is invoked the one which accepts double.

Sub sup = new Sub();
sup.p(int);
sup.p(double);

In this case however, as your subclass doesn't have a method which takes a double, for sup.p(double) call it uses the inherited method from super class if you pass double as an argument.

Upvotes: 2

Peter Lawrey
Peter Lawrey

Reputation: 533520

An int can be widened to a double, but not the other way around. This means that 10 can call B.p(double) or A.p(int) but 10.0 is a double and will not be implicitly converted to an int i.e. only B.p(double) will be called.

Upvotes: 2

Related Questions