[Java notes] polymorphism

Keywords: Java

polymorphic

Before introducing the concept of polymorphism, let's introduce upward transformation as an introduction.

1, Upward transformation

A variable of Person type can point to an instance of Student type:

Person p = new Student();

This is because Student inherits from Person, so it has all the functions of Person. If a variable of Person type points to an instance of Student type and operates on it, there is no problem!

This assignment to safely change a subclass type into a parent or even ancestor type is called upcasting.

The upward transformation is actually to safely change a subtype into a more abstract parent type:

Student s = new Student();
Person p = s; // upcasting, ok
Object o1 = p; // upcasting, ok
Object o2 = s; // upcasting, ok

Note that the inheritance tree is Student > Person > Object, so you can convert the Student type to Person or higher-level Object.

2, Polymorphism

Polymorphism means that the same behavior has multiple different manifestations. That is, for a method call of a certain type, the method it actually executes depends on the method of the actual type at run time. When a method is called on a type, the actual method executed may be an override method of a subclass.

How to achieve polymorphism? There are three necessary conditions for the implementation of polymorphism:

  1. inherit
  2. Override parent method
  3. A parent class reference points to a child class object

For example, the following code:

class Fruit {
    public void eat() {
        System.out.println("eat Fruits");
    }
}

class Apple extends Fruit { // 1. Succession
    @Override
    public void eat() { // 2. Override parent method
        System.out.println("eat Apples");
    }

    public static void main(String[] args) {
        Fruit fruit = new Apple(); // 3. The parent class reference points to the child class object
        fruit.eat(); // eat Apples
    }
}

After executing the main method, the output result is "eat Apples", that is, the actual call is the eat method rewritten by the Apple subclass.

Note: object polymorphism is only applicable to methods, not attributes. That is, even if the field property is modified by the child object, the modification is not visible when the parent object is called.

Function expansion

Polymorphism has a very powerful function, that is, it allows to add more types of subclasses to realize function expansion without modifying the code based on the parent class.

Let's take an example:

class Driver{
    // conn = new MySQlConnection();
    // conn = new OracleConnection();
	public void doData(Connection conn){
		//Operate the data according to the standard steps
//		conn.method1();
//		conn.method2();
//		conn.method3();
	}
}

We have defined a class to operate the database. In the future, it can operate the data of different databases according to the specific incoming Connection objects.

  • Assuming that the incoming object is an object of the MySQL Connection class that inherits the Connection class, it can operate the data in the MySQL database.

We can see that at this time, the Connection class only defines a "specification". When using this Connection class, the methods rewritten by subclass objects are actually used. Therefore, polymorphism is often used with abstract classes / interfaces, because the parent class itself often does not need to have a specific method implementation.

  • In fact, the Connection class provided by Java itself is indeed an interface. When using it, you should import the database API implemented by various database manufacturers.

3, Downward transformation

(1) Why use downward transformation

After the polymorphism of the object, the memory actually loads the properties and methods unique to the subclass. However, because the variable is declared as the parent type, only the properties and methods declared in the parent class can be called during compilation. Subclass specific properties and methods cannot be called (forced call will report an error). How can I call subclass specific properties and methods? We need to use downward transformation.

(2) Downward transformation concept

Contrary to upward transformation, if a parent type is forcibly transformed into a child type, it is downward casting.

We use the cast type converter () to achieve the downward transformation.

(3) Precautions during use

The downward transformation can succeed only when the parent variable points to the child object. For example:

Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();
Student s1 = (Student) p1; // ok
Student s2 = (Student) p2; // runtime error! ClassCastException!
  • Transforming p1 to Student will succeed because p1 does point to the Student instance.

  • Transforming p2 into Student will fail because the actual type of p2 is Person, and the parent class cannot be changed into a child class, because the child class has more functions than the parent class, and more functions cannot be changed out of thin air.

In case of failure, the Java virtual opportunity reports ClassCastException.

(4) How to avoid mistakes

1. Concept of instanceof

To avoid downward transformation errors, Java provides the instanceof operator to determine whether an instance is a specified type or a subclass of this type:

Person p = new Person();
System.out.println(p instanceof Person); // true
System.out.println(p instanceof Student); // false

Student s = new Student();
System.out.println(s instanceof Person); // true
System.out.println(s instanceof Student); // true

Student n = null;
System.out.println(n instanceof Student); // false

Why does instanceof determine true for subclass objects of the specified type? In fact, this is also very easy to understand, because whether you are a student or a teacher, you are a person. If someone says you are not human, you are afraid to jump over and hit him. So instanceof is the same.

If a reference variable is null, the judgment of any instanceof is false.

2. Use of instanceof

Using instanceof, you can judge the following before the downward Transformation:

Person p = new Student();
if (p instanceof Student) {
    // Only by judging success can we transition downward:
    Student s = (Student) p; // It will succeed
}

Starting from Java 14, after judging instanceof, it can be directly transformed into a specified variable to avoid forced transformation again. For example, for the following code:

Object obj = "hello";
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.toUpperCase());
}

It can be rewritten as follows:

Object obj = "hello";
if (obj instanceof String s) {
    // You can directly use the variable s:
    System.out.println(s.toUpperCase());
}

This way of using instanceof is more concise.

(5) Graphic analogy

Posted by Fking on Fri, 26 Nov 2021 12:49:16 -0800