Thoughts of Java Programming Chapter 8 Polymorphism

Keywords: Java Attribute

Records are not comprehensive, only records of their own knowledge points are not clear.
If you have any questions, please comment.

1. Method call binding

Associating a method call with a method body is called a binding.
  • Binding before program execution is called pre-binding (C has only one way to call it, that is pre-binding).
  • At runtime, binding according to the type of object is late binding.

In java, besides static method and final method (private belongs to final method), other methods belong to late binding.

Once we know the fact that all methods in Java are polymorphic through dynamic binding, we can write program code that only deals with classes, and that all exported classes run correctly. Or to put it another way, send a message to an object that decides what to do.

Create multiple classes under one package: package com.example.duotai;

public class Shape {
     public void draw(){}
     public void erase(){}
}
public class Circle extends Shape{
     public void draw(){
          System.out.println("Circle.draw()");
     }
     public void erase() {
          System.out.println("Circle.erase()");
     }
}

public class Square extends Shape{
     public void draw(){
          System.out.println("Square.draw()");
     }
     public void erase(){
          System.out.println("Square.erase()");
     }
}

public class Triangle extends Shape{
     public void draw(){
          System.out.println("Triangle.draw()");
     }
     public void erase(){
          System.out.println("Triangle.erase()");
     }
}

public class RandomShapeGenerator {
     private Random rand=new Random(47);
     public Shape next(){
          int nextInt = rand.nextInt(3);
          switch (nextInt) {
          default:
          case 0:
               return new Circle();
          case 1:
               return new Square();
          case 2:
               return new Triangle();
          }
     }
}

public class Shapes {
     private static RandomShapeGenerator gen=new RandomShapeGenerator();
     public static void main(String[] args) {
          Shape[] s=new Shape[9];
          for(int i=0;i<s.length;i++){
               s[i]=gen.next();
          }
          for(Shape shp:s){
               shp.draw();
          }
     }

}

Operation results:
Triangle.draw()
Triangle.draw()
Square.draw()
Triangle.draw()
Square.draw()
Triangle.draw()
Square.draw()
Triangle.draw()
Circle.draw()

Once you understand the polymorphism mechanism, you may begin to think that everything can be polymorphic. However, only ordinary method calls can be polymorphic. For example, if you access a domain directly, the access will be parsed at compile time. Example:

class Super{
     public int field=0;
     public int getField(){
          return field;
     }
}
class Sub extends Super{
     public int field=1;
     public int getField(){
          return field;
     }
     public int getSuperfield(){
          return super.field;
     }
}
public class FieldAccess {
     public static void main(String[] args) {
          Super sup = new Sub();
          System.out.println("sup.field="+sup.field);
          System.out.println("sup.getField="+sup.getField());
          Sub sub = new Sub();
          System.out.println("sub.field="+sub.field);
          System.out.println("sub.getField="+sub.getField());
          System.out.println("sub.getSuperfield="+sub.getSuperfield());

     }
}

Operation results:
sup.field=0
sup.getField=1
sub.field=1
sub.getField=1
sub.getSuperfield=0

Result analysis:
Consider inheritance as expansion and upward transformation as compression.
The getField method should have used the parent class, but there is a getField method in the subclass, which is actually rewritten.
But field is an attribute, so there are two field attributes in the subclass. After the upward transition, the field attribute of the subclass is compressed and discarded. So in
Super sup = new Sub();
System.out.println("sup.field="+sup.field);
The printed result is the value of the field of the parent class. field = 0;

2. Behavior of polymorphic methods within constructors

   The hierarchy of constructor calls presents an interesting dilemma. What happens if a dynamic binding method of the object being constructed is called inside a constructor?
   Within general methods, dynamic binding calls are determined at runtime because the object cannot know whether it belongs to the class in which the method belongs or to the derived class of that class.
   Conceptually, the constructor's job is actually to create objects (which is not a normal job). Within any constructor, this object may only be partially formed -- we only know that the base class object has been initialized.
class Glyph{
     void draw(){
          System.out.println("Glyph.draw()");
     }
     Glyph(){
          System.out.println("Glyph() before draw()");
          draw();
          System.out.println("Glyph() after draw()");
     }
}
class RoundGlyph extends Glyph{
     private int radius=1;
     RoundGlyph(int r){
          radius=r;
          System.out.println("RoundGlyph.RoundGlyph().radius="+radius);
     }
     void draw(){
          System.out.println("RoundGlyph.draw().radius="+radius);
     }
}
public class PolyConstructors {
     public static void main(String[] args) {
          new RoundGlyph(5);
     }

}

Operation results:
Glyph() before draw()
RoundGlyph.draw().radius=0
Glyph() after draw()
RoundGlyph.RoundGlyph().radius=5

As you can see from the results, RoundGlyph.draw().radius=0 is because the method of the derived class is called in the construction method of the base class, but the members of the derived class have not been initialized. So zero. The whole program looks logical and compiles well, but the results are unexpected, so it's important to note that the only methods that can be safely invoked in the constructor are final methods in the base class (also applicable to private methods, which automatically belong to final methods). These methods can't be covered, so there won't be any of these surprising problems.

The actual order of initialization:

 1. Initialize the allocated object's storage space to binary zero before anything else happens.
 2. Recursively invoke the constructor of the base class as above.
 3. Initialization methods of members are invoked in declarative order.
 4. Call the constructor body of the exported class.

If the object is to be destroyed, the destroy order should be the opposite of the initialization order.

Posted by dustinkrysak on Fri, 24 May 2019 15:25:59 -0700