Detailed Explanation of Java Polymorphism —— Parent Class Reference to Subclass Object - Java Programming - Programming Development - Eden Network

by henxue on 2010-07-16 14:08:21

Object-oriented programming has three characteristics: encapsulation, inheritance, and polymorphism.

Encapsulation hides the internal implementation details of a class, allowing the internal structure of the class to be changed without affecting its users while also protecting data.

Inheritance is used to reuse parent class code and prepare for the implementation of polymorphism. So, what is polymorphism?

Method overriding, overloading, and dynamic linking form polymorphism. One reason Java introduced the concept of polymorphism is that it differs from C++ in terms of class inheritance. The latter allows multiple inheritance, which indeed provides very powerful functionality, but complex inheritance relationships also bring more trouble to C++ developers. To avoid risks, Java only allows single inheritance, where derived classes have an IS-A relationship with base classes (for example, "cat" IS-A "animal"). Although this ensures simple and clear inheritance relationships, it inevitably imposes significant functional restrictions. Therefore, Java introduces the concept of polymorphism to make up for this deficiency. Additionally, abstract classes and interfaces are also important means to solve the limitations imposed by single inheritance rules. At the same time, polymorphism is the essence of object-oriented programming.

To understand polymorphism, you first need to know what "upcasting" is.

If I define a subclass `Cat` that inherits from the `Animal` class, then the latter is the parent class of the former. I can instantiate a `Cat` object like this:

```java

Cat c = new Cat();

```

This isn't hard to understand. But when I define it like this:

```java

Animal a = new Cat();

```

What does this mean?

It's quite simple. It means I've defined an `Animal` type reference pointing to a newly created `Cat` type object. Since `Cat` inherits from its parent class `Animal`, an `Animal` type reference can point to a `Cat` type object. What is the significance of doing this? Because the subclass improves and expands upon the parent class, the subclass generally has more powerful functionality and more unique attributes.

Defining a parent class type reference pointing to a subclass object allows the use of the powerful features of the subclass while extracting the commonality of the parent class.

Thus, a parent class type reference can call all the properties and methods defined in the parent class. However, for methods defined in the subclass but not in the parent class, it is powerless;

At the same time, a method in the parent class can only be called by a parent class type reference if it is defined in the parent class and not overridden in the subclass;

For methods defined in the parent class, if they are overridden in the subclass, then the parent class type reference will call the overridden method in the subclass, which is dynamic linking.

Consider the following program:

```java

class Father {

public void func1() {

func2();

}

// This is the func2() method in the parent class. Since it is overridden in the subclass below,

// it will no longer be effective when called via a parent class type reference.

// Instead, the overridden func2() method in the subclass will be called.

public void func2() {

System.out.println("AAA");

}

}

class Child extends Father {

// func1(int i) is an overloaded version of the func1() method.

// Since it is not defined in the parent class, it cannot be called by a parent class type reference.

// Therefore, child.func1(68) in the main method below is incorrect.

public void func1(int i) {

System.out.println("BBB");

}

// func2() overrides the func2() method in the parent class Father.

// If the func2() method is called via a parent class type reference, it will necessarily be the overridden func2() method in the subclass.

public void func2() {

System.out.println("CCC");

}

}

public class PolymorphismTest {

public static void main(String[] args) {

Father child = new Child();

child.func1(); // What will the output be?

}

}

```

The above program is a typical example of polymorphism. The subclass `Child` inherits from the parent class `Father` and overrides the `func1()` method and rewrites the `func2()` method. After overloading, `func1(int i)` and `func1()` are no longer the same method. Since the parent class does not have `func1(int i)`, the parent class type reference `child` cannot call the `func1(int i)` method. And since the subclass rewrites the `func2()` method, when the parent class type reference `child` calls this method, it will call the rewritten `func2()` in the subclass.

So, what result will this program print?

Obviously, it should be "CCC".

For polymorphism, it can be summarized as follows:

1. Use a parent class type reference pointing to a subclass object;

2. The reference can only call methods and variables defined in the parent class;

3. If a method in the subclass overrides a method in the parent class, then when calling this method, it will call the overridden method in the subclass; (dynamic linking, dynamic invocation)

4. Variables cannot be overridden ("overwritten"), the concept of "override" only applies to methods. If a subclass "overrides" a variable in the parent class, there will be a compilation error.

Polymorphism is realized through:

1. Interfaces and different classes implementing and overriding the same method in the interface.

2. Parent classes and different subclasses inheriting and overriding the same method in the parent class.

Basic Concepts:

Polymorphism: Sending a message to an object, letting the object itself decide how to respond.

Dynamic method invocation is achieved by assigning a subclass object reference to a superclass object reference variable.

Java's mechanism follows this principle: When a superclass object reference variable refers to a subclass object, the type of the referenced object rather than the reference variable determines which member method to call, but the called method must be defined in the superclass, meaning it is a method overridden by the subclass.

1. If `a` is a reference to class `A`, then `a` can point to an instance of class `A` or one of its subclasses.

2. If `a` is a reference to interface `A`, then `a` must point to an instance of a class that implements interface `A`.

Java Polymorphism Implementation Mechanism:

Currently, SUN's JVM implementation mechanism involves references to class instances being pointers to handles. These handles consist of two pointers:

One pointer points to a table, which itself contains two pointers (one pointing to a table containing the object's methods, the other pointing to the class object indicating the type of the object);

Another pointer points to a block of memory allocated from the Java heap.

Summary:

1. Dynamic method invocation is achieved by assigning a subclass object reference to a superclass object reference variable.

```java

DerivedC c2 = new DerivedC();

BaseClass a1 = c2; // BaseClass is the base class, DerivedC is a subclass of BaseClass

a1.play(); // play() is defined in both BaseClass and DerivedC, i.e., the subclass overrides this method

```

Analysis:

* Why can an object of a subclass type be assigned to a superclass reference?

Automatic upcasting occurs. Through this statement, the compiler automatically moves the subclass instance upward, converting it into the generic type BaseClass.

* Will `a.play()` execute the method defined in the subclass or the superclass?

In the runtime period, the actual type of the object reference `a` will determine the corresponding method to be called. This is why polymorphism exists. A base class object reference, when assigned different subclass object references, will exhibit different behaviors when executing the method.

When `a1 = c2`, there still exist two handles, `a1` and `c2`, but `a1` and `c2` share the same data memory block and different function tables.

2. You cannot assign a superclass object reference to a subclass object reference variable.

```java

BaseClass a2 = new BaseClass();

DerivedC c1 = a2; // Error

```

In Java, upcasting is performed automatically, but downcasting requires explicit casting.

```java

c1 = (DerivedC)a2; // Perform explicit casting, i.e., downcasting.

```

3. Remember a simple yet complex rule: A type reference can only reference methods and variables contained within its own type.

You might say this rule is incorrect because when a parent class reference points to a subclass object, the method executed is the one in the subclass.

Actually, this is not contradictory. That is due to late binding. During dynamic execution, the method in the subclass is called based on the type. If the method in the subclass is not defined in the parent class, an error will occur.

For example, the `DerivedC` class defines additional functions (such as `myFun()`) beyond those inherited from `BaseClass`.

Analysis:

When using a parent class reference pointing to a subclass, the JVM already uses compiler-generated type information for adjustment conversion.

Here, you can think of it as setting functions not contained in the parent class as invisible in the virtual function table. Note that some function addresses in the virtual function table may have been rewritten in the subclass, so the address items in the object's virtual function table have been set to the address of the method body completed in the subclass.

4. Comparison of Polymorphism in Java and C++

The JVM's approach to supporting polymorphism is almost identical to that in C++, except that in C++, compilers often place type information and virtual function information in a single virtual function table, distinguishing them using some technique.

Java separates type information and function information. In Java, after inheritance, the subclass resets its own virtual function table, which consists of two parts: virtual functions inherited from the parent class and virtual functions of the subclass itself.

Virtual function calls are indirectly invoked through the virtual function table, thus enabling polymorphism.

All functions in Java, except those declared as `final`, use late binding.

---

5. One behavior, different objects, and they manifest differently.

For example:

Method Overloading (`overloading`) and Method Overriding (`override`).

```java

class Human {

void run() { System.out.println("Person is running"); }

}

class Man extends Human {

void run() { System.out.println("Man is running"); }

}

```

Here, the same "run" behavior differs depending on the object (this is an example of method overriding).

```java

class Test {

void out(String str) { System.out.println(str); }

void out(int i) { System.out.println(i); }

}

```

This is an example of method overloading: the method names are the same, but the parameter lists differ.

Ok, understanding these concepts is not enough. Let's use the "person running" example again.

```java

Human ahuman = new Man();

```

This creates a `Man` object and declares a `Human` reference pointing to the `Man` object. Essentially, it treats the `Man` object as a `Human`.

For example, at the zoo, you see an animal and don't know what it is:

"What is this animal?" "It's a giant panda!"

These two sentences are the best proof because, even though you don't know it's a giant panda, you know its superclass is an animal. Therefore, treating this giant panda object as its superclass Animal is reasonable.

Under this circumstance, note that `new Man()` actually instantiates a `Man` object, so `ahuman.run()` outputs "Man is running."

If in the subclass `Man`, you define some unique methods such as `eat()`, and `Human` does not have this method, when calling the `eat()` method, you must perform a type cast: `((Man)ahuman).eat()`.

For interfaces, the situation is similar...

Example:

```java

package domatic;

// Define superclass superA

class superA {

int i = 100;

void fun(int j) {

j = i;

System.out.println("This is superA");

}

}

// Define subclass subB of superA

class subB extends superA {

int m = 1;

void fun(int aa) {

System.out.println("This is subB");

}

}

// Define subclass subC of superA

class subC extends superA {

int n = 1;

void fun(int cc) {

System.out.println("This is subC");

}

}

class Test {

public static void main(String[] args) {

superA a = new superA();

subB b = new subB();

subC c = new subC();

a = b;

a.fun(100);

a = c;

a.fun(200);

}

}

```

/*

* In the above code, `subB` and `subC` are subclasses of the superclass `superA`. In class `Test`, we declare three reference variables `a`, `b`, and `c`.

* We achieve dynamic method invocation by assigning subclass object references to superclass object reference variables. Someone might ask:

* "Why don't (1) and (2) output 'This is superA'?"

* Java's mechanism follows this principle: When a superclass object reference variable refers to a subclass object,

* the type of the referenced object, not the reference variable, determines which member method to call.

* However, the called method must be defined in the superclass,

* meaning it is a method overridden by the subclass.

* Therefore, don't be misled by (1) and (2) in the example above. Although written as `a.fun()`, since `a` is assigned `b`,

* it points to an instance of the subclass `subB`, so (1) actually calls the `fun()` method of the subclass `subB`,

* which overrides the `fun()` method of the superclass `superA`; similarly, (2) calls the `fun()` method of the subclass `subC`.

* Additionally, if the subclass inherits from an abstract class, although abstract classes cannot be instantiated using the `new` operator,

* you can create an abstract class object reference pointing to a subclass object to achieve runtime polymorphism. The specific implementation method is the same as the example above.

* However, the subclass must override all abstract methods in the superclass,

* otherwise, the subclass must be modified with the `abstract` modifier and thus cannot be instantiated.

*/

Most of the above examples demonstrate polymorphism by overriding methods in subclasses. Below is another way to implement polymorphism —— by rewriting parent class methods.

1. JAVA does not support multiple inheritance; a class can only have one parent class. The manifestation of inheritance is polymorphism. A parent class can have multiple subclasses, and in each subclass, you can rewrite the parent class's methods (e.g., `print()`), so the rewritten code in each subclass is different, leading to different manifestations. Thus, using a parent class variable to reference different subclasses, when calling the same method `print()`, the results and manifestations will differ, which is polymorphism —— the same message (i.e., calling the same method) yields different results. Here's an example:

```java

// Parent class

public class Father {

// Parent class has a hitChild method

public void hitChild() {

}

}

// Subclass 1

public class Son1 extends Father {

// Override the hitChild method of the parent class

public void hitChild() {

System.out.println("Why are you hitting me? What did I do wrong!");

}

}

// Subclass 2

public class Son2 extends Father {

// Override the hitChild method of the parent class

public void hitChild() {

System.out.println("I know I was wrong, please stop hitting me!");

}

}

// Subclass 3

public class Son3 extends Father {

// Override the hitChild method of the parent class

public void hitChild() {

System.out.println("I'm running, you can't hit me!");

}

}

// Test class

public class Test {

public static void main(String args[]) {

Father father;

father = new Son1();

father.hitChild();

father = new Son2();

father.hitChild();

father = new Son3();

father.hitChild();

}

}

```

All call the same method but produce different results! This is the manifestation of polymorphism!

Original Article URL: [Eden Network](http://www.edenw.com/tech/devdeloper/java/2010-07-16/4751.html)