JavaSE-No.8 - Polymorphism of the Three Features of Java

JavaSE Portal

JavaSE_Start

JavaSE-No.7.1 - Java's inheritance (super keyword)

JavaSE-No.7.2 - Java's Inheritance (and Composition)

content

1. The concept of polymorphism

In layman's terms, it is a variety of forms. The specific point is to complete a certain behavior. When different objects are completed, different states will be generated.


In general: the same thing, happening to different objects, will produce different results.

2. The realization conditions of polymorphism

To achieve polymorphism in java, the following conditions must be met:

  1. Under the inheritance system, complete the upward transformation
  2. Subclass overrides parent class method
  3. Invoke an overridden method with a reference to the parent class

We first create a parent class Flower:

class Flower {
    private String name;
    private String color;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public Flower(String name, String color) {//Construction method
        this.name = name;
        this.color = color;
    }

    @Override
    public String toString() {
        return "Flower{" +
                "name='" + name + ''' +
                ", color='" + color + ''' +
                '}';
    }

    public void flowering() {
        System.out.println(name+"Flowering??");
    }
}

Let's write two more subclasses CherryBlossom and Rose:

class CherryBlossom extends Flower{
    public CherryBlossom(String name, String color) {
        super(name, color);
    }

    public void view() {
        System.out.println("watch"+getName()+"??");
    }
}
class Rose extends Flower{
    public Rose(String name, String color) {
        super(name, color);
    }
    
    public void gift() {
        System.out.println("a bunch"+getName()+"??As a gift");
    }
}

What is an upward transition?

Assign the subclass object (eg: Rose) to the parent class object (eg: Flower).

public class Test {
    public static void main(String[] args) {
        Rose rose = new Rose("White Rose","white");
        Flower flower1 = rose;//Upward transformation
        
        //shorthand
        Flower flower = new Rose("Red rose","red");
    }
}

We use the newly defined flower to call the methods of the subclass and the parent class respectively. We found that an error was reported when calling the method specific to the subclass.

Because: flower is of type Flower, flower can only call methods in Flower.

rewrite

Override: Also known as overriding. Overriding means that the subclass rewrites the implementation process of the parent class's non-static, non-private modification, non-final modification, non-construction method, etc., and the return value and formal parameters cannot be changed. That is, the shell remains unchanged, and the core is rewritten! The advantage of overriding is that subclasses can define their own specific behavior as needed.

rewritten rules

  1. Generally, the subclass is the same as the superclass method prototype: the modifier return value type method name (parameter list) must be exactly the same

  2. The return value type of the overridden method can be different, but must have a parent-child relationship.

    The return value of the overridden method of the subclass and the superclass forms a covariant type (the return value of the subclass and the return value of the superclass form a parent-child relationship)

  3. The methods and constructors of the parent class that are modified by static and private cannot be overridden.

  4. The access rights are greater than or equal to the access rights of the overridden method in the parent class.

# Note # If a method does not want to be overridden, use final modification (sealed method).

Next, we want the subclass to rewrite the method of the parent class (for example: flowering)

//Rewriting flowering in class CherryBlossom
public void flowering() {
    System.out.println(getName()+"opened??");
}

//Override flowering in class Rose
public void flowering() {
    System.out.println(getName()+"opened??");
}

Invoke an overridden method with a reference to the parent class

What happens when we call the flowering method again?

public class Test {
    public static void main(String[] args) {
        Flower flower3 = new Flower("flower field","colorful");
        flower3.flowering();
        
        Flower flower2 = new CherryBlossom("Cherry blossoms","pink");
        flower2.flowering();//dynamic binding
        
        Flower flower1 = new Rose("Red rose","red");
        flower1.flowering();//dynamic binding
    }
}

Running result display: The variable after the up-transformation calls the flower of the subclass, and dynamic binding occurs (what is dynamic binding will be introduced later in this article)

3. Up and down transitions

3.1 Upward Transformation

direct assignment

public class Test {
    public static void main(String[] args) {
        
        Flower flower = new Rose("Red rose","red");
        flower.flowering();
    }
}

method parameters

public class Test {
    public static void func(Flower flower) {
        flower.flowering();
    }
    public static void main(String[] args) {
        func(new Rose("Red rose","red"));
        func1(new CherryBlossom("Cherry blossoms","pink"));
    }
}

Running results show:

method return value

public class Test {
    public static Flower func() {
        return new Rose("Red rose","red");
    }
    
    public static void main(String[] args) {
         Flower flower = func();
    }
}

The advantages of upcasting: make the code implementation simpler and more flexible.

Defect of upcasting: Cannot call subclass-specific methods.

3.2 Downcasting

After a subclass object is upwardly transformed and used as a method of the parent class, the method of the subclass can no longer be called, but sometimes it may be necessary to call the method specific to the subclass. In this case, restore the reference of the parent class to the subclass object. , which is down-converted.

public static void main(String[] args) {
    Flower flower = new Rose("Red rose","red");
    Rose rose = (Rose) flower;//downcast
    rose.gift();
}

# Note # Downcasting is used less frequently and is not safe. If the conversion fails, an exception will be thrown at runtime.

Let's look at a piece of error code.

public static void main(String[] args) {
    Flower flower = new CherryBlossom("Cherry blossoms","pink");
    Rose rose = (Rose) flower;//Downward transformation, is the cherry blossom a rose? ?
    rose.gift();
}

instanceof

Not all flowers are "roses". In order to improve the safety of downcasting in Java, instanceof is introduced. If the expression is true, it can be safely converted.

public static void main(String[] args) {
        Flower flower = new CherryBlossom("Cherry blossoms","pink");
        //Determine whether flower refers to the Rose object
        if (flower instanceof Rose) {
            Rose rose = (Rose) flower;//downcast
            rose.gift();
        }
        flower = new Rose("Red rose","red");
        if (flower instanceof Rose) {
            Rose rose = (Rose) flower;//downcast
            rose.gift();
        }
}

4. Rewritten design rules

For classes that are already in use, try not to modify them. The best way is: redefine a new class to reuse common content, and add or change new content.

For example: A few years ago, the caller ID can only display the number, and today's mobile phone can display not only the number, but also the avatar, the region, etc. when the caller ID is displayed. In this process, we should not modify the original old class, because the original class may still be used by users. The correct way is: create a new mobile phone class and rewrite the caller ID method. , which meets our current needs.

5. Dynamic binding and static binding

5.1 Dynamic binding

Dynamic binding: Also known as late binding (late binding), that is, at compile time, the behavior of the method cannot be determined, and it is necessary to wait until the program runs to determine the method of which class to specifically call.

Continuing the above code, we write the following two lines of code in main:

public class Test {
    public static void main(String[] args) {
    
        Flower flower = new Rose("Red rose","red");
        flower.flowering();
    }
}
//operation result
 red rose blooming??

We open the Powershell window and use javap -c Test to decompile it.

When compiling, the method of the parent class is called, but at runtime, the program is dynamically bound and the method of the subclass is called.

# Notes # Dynamic binding also occurs in the constructor of the parent class. But don't write like that! !

class A {
    public A() {
        print();
    }
    public void print() {
        System.out.println("A");
    }
}
class B extends A {
    public void print() {
        System.out.println("B");
    }
}

public class Example {
    public static void main(String[] args) {
        B bb = new B();
    }
}
//operation result
B

Conclusion: "Make the object into a working state in the simplest way possible", try not to call the method in the constructor, there may be some hidden but extremely difficult to find problems.

5.2 Static binding

Static binding: Also known as early binding (early binding), that is, at compile time, the specific method to be called is determined according to the type of actual parameters passed by the user. Typically represents function overloading.

public class Example {
    public static void print() {
        System.out.println("hh");
    }

    public static void print(int a) {
        System.out.println("haha");
    }
    
    public static void main(String[] args) {
        //Static binding: When compiling, the final method to be called is determined
        print();
        print(1);
    }
}

6. Advantages and disadvantages of polymorphism

The benefits of using polymorphism

  1. Can reduce the "cyclomatic complexity" of the code, avoid using a lot of if - else

Little knowledge: Cyclomatic complexity

Cyclomatic complexity is a way of describing the complexity of a piece of code. If a piece of code has many conditional branches or loops, it is considered more complicated to understand.

Therefore, we can simply and rudely calculate the number of conditional statements and loop statements in a piece of code, which is called "cyclomatic complexity".

Let's rewrite the code and write several new subclasses:

class Flower {
    public String name;
    public String color;

    public void flowering() {
        System.out.println("flowers are blooming??");
    }
}
class CherryBlossom extends Flower{
    @Override
    public void flowering() {
        System.out.println("??opened");
    }
}
class Rose extends Flower{
    @Override
    public void flowering() {
        System.out.println("??opened");
    }
}
class Sunflower extends Flower{
    @Override
    public void flowering() {
        System.out.println("??opened");
    }
}
class Tulip extends Flower{
    @Override
    public void flowering() {
        System.out.println("??opened");
    }
}

If we want ?? to bloom in the order of "???", how should we write if-else? We can see that it is very cumbersome.

public class Test {
    public static void func() {
        CherryBlossom cherryBlossom = new CherryBlossom();
        Rose rose = new Rose();
        Sunflower sunflower = new Sunflower();
        Tulip tulip = new Tulip();
        //"????????????"
        String[] flowers = {"cherryBlossom","sunflower","tulip","rose","sunflower","tulip"};
        for (String flower:flowers) {
            if (flower.equals("cherryBlossom")) {
                cherryBlossom.flowering();
            } else if (flower.equals("sunflower")) {
                sunflower.flowering();
            } else if (flower.equals("rose")) {
                rose.flowering();
            } else if (flower.equals("tulip")) {
                tulip.flowering();
            }
        }
    }
}

If we use polymorphism:

public class Test {
    public static void func() {
        CherryBlossom cherryBlossom = new CherryBlossom();
        Rose rose = new Rose();
        Sunflower sunflower = new Sunflower();
        Tulip tulip = new Tulip();
        //"????????????"
        Flower[] flowers = {cherryBlossom,sunflower,tulip,rose,sunflower,tulip};
        for (Flower flower:flowers) {
            flower.flowering();
        }
    }
}
  1. More scalable

    If you want to add a new shape, the cost of code changes is relatively low using polymorphism

    For the caller of the class (the func method), it only needs to create an instance of the new class, and the change cost is very low.

Polymorphism defect: the code runs less efficiently.

??(( д)(д ))??

The above is what I will talk about today. I hope it will be helpful to everyone. If you have any questions, please comment and point out, and we will actively correct them! ! The next article will describe the relevant knowledge of Java abstract classes. I look forward to your support, thank you~

Tags: Java Back-end Interview Programmer programming language

Posted by Big X on Fri, 20 May 2022 00:12:51 +0300