More on Inheritance & Subtyping polymorphism

Share on:

Overview

Let' see some examples of various cases of Inheritance & polymorphism.

Example - 1 (Very basic)

 1class A {
 2
 3    public void func() {
 4
 5        System.out.println("Class A's func");
 6    }
 7
 8    public void foo() {
 9
10        System.out.println("Class A's foo");
11    }
12}
1class Helper {
2
3    public static void main(String args[]) {
4
5        A a = new A();
6        a.foo();
7        a.func();
8    }
9}
Let's look at it visually: Example 1 Output:
1Class A's foo
2Class A's func()
Let's understand the output:

  1. foo() is defined in class A. We are creating an object of class A and using reference type of class A only. a will be able to access foo() and print Class A's foo
  2. The same logic applies for func()

Example 2

 1class A {
 2
 3    public void func() {
 4
 5        System.out.println("Class A's func");
 6    }
 7
 8    public void foo() {
 9
10        System.out.println("Class A's foo");
11    }
12}
1class B extends A {
2
3    public void bar() {
4
5        System.out.println("Class B's bar");
6    }
7}
 1class Helper {
 2
 3    public static void main(String args[]) {
 4
 5        B b = new B();
 6        b.foo();
 7        b.func();
 8        b.bar();
 9    }
10}
Let's look at it visually: Example 2 Output:
1Class A's foo
2Class A's func
3Class B's bar
Let's understand the output:

  1. foo() is defined in class A. We are creating an object of class B and using reference type of class B only. b will be able to access foo() through inheritance and print Class A's foo
  2. func() is defined in class A. We are creating an object of class B and using reference type of class B only. b will be able to access func() through inheritance and print Class A's func
  3. bar() is defined in class B. We are creating an object of class B and using reference type of class B only. b will be able to access bar() and print Class B's bar

Example 3

 1class A {
 2
 3    public void func() {
 4
 5        System.out.println("Class A's func");
 6    }
 7
 8    public void foo() {
 9
10        System.out.println("Class A's foo");
11    }
12}
1class B extends A {
2
3    public void bar() {
4
5        System.out.println("Class B's bar");
6    }
7}
 1class Helper {
 2
 3    public static void main(String args[]) {
 4
 5        A a = new B();
 6        a.foo();
 7        a.func();
 8        //a.bar();
 9    }
10}
Let's look at it visually: Example 3 Very carefully notice the arrow shift from example 2 & 3. Output:
1Class A's foo
2Class A's func
Let's understand the output:

  1. foo() is defined in class A. We are creating an object of class B and using reference type of class A. a will be able to access foo() through inheritance and print Class A's foo
  2. func() is defined in class A. We are creating an object of class B and using reference type of class A. a will be able to access func() through inheritance and print Class A's func
  3. What if I try to print a.bar()? Now notice where arrow is pointed to in the figure above. bar() is not defined in class A. So even if I've created an object of class B, I will not be able to access bar() method from reference type of class A. a.bar() will throw me error! (Something new)

Example 4

 1class A {
 2
 3    public void func() {
 4
 5        System.out.println("Class A's func");
 6    }
 7
 8    public void foo() {
 9
10        System.out.println("Class A's foo");
11    }
12}
 1class B extends A {
 2
 3    public void bar() {
 4
 5        System.out.println("Class B's bar");
 6    }
 7    public void func() {
 8
 9        System.out.println("Class B's func");
10    }
11}
 1class Helper {
 2
 3    public static void main(String args[]) {
 4
 5        B b = new B();
 6        b.foo();
 7        b.func();
 8        b.bar();
 9    }
10}
Now we are overriding func() in B. Let's look at it visually: Example 4 Output:
1Class A's foo
2Class B's func
3Class B's bar
Let's understand the output:

  1. foo() is defined in class A. We are creating an object of class B and using reference type of class B. b will be able to access foo() through inheritance and print Class A's foo
  2. func() is defined in class A and also overridden in B. We are creating an object of class B and using reference type of class B. b will be able to access func() and print Class B's func
  3. bar() is defined in class B. We are creating an object of class B and using reference type of class B only. b will be able to access bar() and print Class B's bar

Example 5

 1class A {
 2
 3    public void func() {
 4
 5        System.out.println("Class A's func");
 6    }
 7
 8    public void foo() {
 9
10        System.out.println("Class A's foo");
11    }
12}
 1class B extends A {
 2
 3    public void bar() {
 4
 5        System.out.println("Class B's bar");
 6    }
 7    public void func() {
 8
 9        System.out.println("Class B's func");
10    }
11}
 1class Helper {
 2
 3    public static void main(String args[]) {
 4
 5        A a = new B();
 6        a.foo();
 7        a.func();
 8        //a.bar();
 9    }
10}
Now we are overriding func() in B. Let's look at it visually: Example 5 Output:
1Class A's foo
2Class B's func
Let's understand the output:

  1. foo() is defined in class A. We are creating an object of class B and using reference type of class A. a will be able to access foo() through inheritance and print Class A's foo
  2. func() is defined in class A and also overridden in B. We are creating an object of class B and using reference type of class A. a will be able to access func(). You might be tempted to say that it'll print Class A's func but it will be wrong. Visually, it will follow the arrows and print Class B's func
  3. Still the same logic applies for a.bar() (as example 3). Reference of type A will not be able to access bar()

Example 6

1class A {
2
3    public int a;
4}
1class B extends A {
2
3    public int b;
4}
 1class Helper {
 2
 3    public static void main(String args[]) {
 4
 5        B bObject = new B();
 6        bObject.a = 1;
 7        bObject.b = 2;
 8
 9        System.out.println("a: " + bObject.a);
10        System.out.println("b: " + bObject.b);
11    }
12}
Let's look at it visually: Example 6 Output:
1a: 1
2b: 2
Let's understand the output:

  1. a is an instance variable defined in class A. We are creating an object bObject of class B and using reference type of class B only to refer it. a will be available in class B through inheritance and we will be able to print a: 1
  2. b is an instance variable of class B itself, so this shouldn't be a problem.

Example 7

1class A {
2
3    public int a;
4}
1class B extends A {
2
3    public int b;
4}
 1class Helper {
 2
 3    public static void main(String args[]) {
 4
 5        A aObject = new B();
 6        aObject.a = 1;
 7        //aObject.b = 2;
 8
 9        System.out.println("a: " + aObject.a);
10        //System.out.println("b: " + aObject.b);
11    }
12}
Now we are referring to B's object using class A reference. Let's look at it visually: Example 7 Notice the change in arrow pointers here as well. Output:
1a: 1
Let's understand the output:

  1. a is an instance variable defined in class A. We are creating an object aObject of class B and using reference type of class B only to refer it. a will be available in class B through inheritance and we will be able to print a: 1
  2. b is an instance variable of class B. We are creating an object aObject of class B and using reference type of class A to refer it. It won't be visible to class A and hence it will throw errors (similar logic as example 3)

Example 8 (hiding variable)

1class A {
2
3    public int a = 0;
4}
1class B extends A {
2
3    public int a = 1;
4}
 1class Helper {
 2
 3    public static void main(String args[]) {
 4
 5        B bObject = new B();
 6        A aObject = new A();
 7
 8        System.out.println("Output 1: " + bObject.a);
 9        System.out.println("Output 2: " + aObject.a);
10    }
11}
This looks like something similar to Example 4. We expect both lines to print 1 as we've overridden a instance variable in B. Let's look at it visually: Let's look at it visually: Example 8 Output:
1Output 1: 1
2Output 2: 0
It turns out that we are wrong. Within the entire scope of the B class, the superclass field of the same name is hidden. So anytime we reference the simple name a, or the scoped name this.a, within the class, we are referring to the field that is defined in B. As for the second line: There is no polymorphism for fields in Java so accessing a using class A reference, even though we are creating an object of class B, will point to class A's instance variable only.

Takeaways

  1. You can call a method on an object only if the class of the reference variable has that method.
  2. No polymorphism when it comes to instance variables

Acknowledgements

  1. https://stackoverflow.com/questions/15513467/polymorphism-with-instance-variables
  2. https://stackoverflow.com/questions/10722110/overriding-member-variables-in-java-variable-hiding
comments powered by Disqus