- In object-oriented programming languages, polymorphism is the third basic feature after data abstraction and inheritance.
- Polymorphism separates the interface from the implementation from another perspective by separating what to do and how to do it.
- Encapsulation creates new data types by merging characteristics and behaviors. Implementation hiding separates the interface from the implementation by “privatizing” the details, while polymorphism decouples the coupling between types.
On upward transformation & turning point
- An object can be used as its own type or as its base class, and the practice of treating a reference to an object as a reference to its base class is called “upcasting.”
- Associating a method call with a method body is calledThe binding.
- Binding before program execution (implemented by the compiler and linker, if any) is called pre-binding.
- Binding based on the type of the object at run time is called late binding, also known as dynamic binding or run time binding.
- All Java methods except static and final methods (private methods are final methods) are late bound.
- After Java implements polymorphism with dynamic binding, we can write only code related to the base class, and this code will work correctly for all derived classes of that base class.
- See Exercise 2 for examples of polymorphism.
- In a well-designed OOP program, most or all of the methods communicate only with the base class interface. Such programs are extensible because new data types can be inherited from common base classes to add new functionality.
- The domain has no polymorphism.
- If a method is static, its behavior is not polymorphic. Static methods are associated with classes, not individual objects.
- Since final methods are not overridden, private methods are also not overridden, so there is no way to dynamically bind. That is, only non-private methods can be overridden, but “overriding” private methods does not generate an error, but the result is often not as expected:
package com.company.ch08; public class PrivateOverride { private void func() { System.out.println("private func()"); } public static void main(String[] args) { PrivateOverride privateOverride = new Derived(); privateOverride.func(); }} Class Derived extends PrivateOverride {public void func() { System.out.println("Derived func()"); } } // private func()Copy the code
Constructor and polymorphism
Constructors are not polymorphic; they are actually static methods, but static is implicitly declared.
The order in which the constructor is called
- The constructors of the base class are always called during the construction of the exported class, and are gradually linked up through the inheritance hierarchy so that the constructors of each base class are called.
- In the constructor body of the exported class, if a base class constructor is not explicitly called, it silently calls the default constructor. If there is no default constructor, the compiler will fail. (If a class has no constructor, the compiler will add a default constructor.)
The order in which constructors are called:
- Invoke the base class constructor.
- Call the initialization methods of members in the declared order.
- Calls the body of the constructor of the exported class.
Inheritance and cleanup
Garbage collection takes care of most of the problems, but if you do need to do a cleanup, you need to manually call a specific function to do it. For inheritance reasons, we need to call the base-class version of the cleanup function when overriding the base-class cleanup function. Usually at the end of the exported class cleanup function. If a member object also needs to be cleaned up, the member’s cleanup function needs to be called in the cleanup function. The principle of invocation is that the order of cleanup should be the same as the order of initialization.
We can use “reference counting” to track the number of objects accessing the shared object (shared_ptr in C++) instead of simply calling the cleanup function if the member object is shared by one or more other objects.
The behavior of polymorphic methods inside the constructor:
What happens if one of the dynamically bound methods of the object being constructed is called inside a constructor?
package com.company.ch08;
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);
}
@Override
void draw() {
System.out.println("RoundGlyph.draw(), radius = " + radius);
}
}
public class PolyConstructors {
public static void main(String[] args) {
new RoundGlyph(5);
}
}
// Glyph() before draw()
// RoundGlyph.draw(), radius = 0
// Glyph() after draw()
// RoundGlyph.RoundGlyph(), radius = 5Copy the code
As you can see from the output above, calling a dynamic method in a base class does call the corresponding method of the exported class, but the exported class’s domain is not fully initialized.
The procedure for initializing an instance:
- Initialize the storage allocated to an object to binary zeros before anything else happens
- Invoke the base class constructor
- Call the initialization methods of members in declared order
- Invoke the constructor topic of the exported class.
The only methods that can be safely called ina constructor are final methods in the base class (private methods are final methods).
Covariant return type
A covariant return type was added to Java SE5, which represents an export type that an overridden method in an exported class can return the return type of a base class method.
package com.company.ch08; class Grain { @Override public String toString() { return "Grain"; } } class Wheat extends Grain { @Override public String toString() { return "Wheat"; } } class Mill { Grain process() { return new Grain(); }} class WheatMill extends Mill {@override Wheat process() { Wheat return new Wheat(); } } public class CovariantReturn { public static void main(String[] args) { Mill mill = new Mill(); Grain grain = mill.process(); System.out.println("grain = " + grain); mill = new WheatMill(); grain = mill.process(); System.out.println("grain = " + grain); } } // grain = Grain // grain = WheatCopy the code
Design with inheritance
We should choose “combination” first, especially if we are not quite sure which approach to use. Composition does not force our program who is called into an inherited hierarchy. Also, the combination is more flexible, and he can dynamically choose the type.
package com.company.ch08;
class Actor {
public void act() {}
}
class HappyActor extends Actor {
@Override
public void act() {
System.out.println("HappyActor");
}
}
class SadActor extends Actor {
@Override
public void act() {
System.out.println("SadActor");
}
}
class Stage {
private Actor actor = new HappyActor();
public void change() {
actor = new SadActor();
}
public void performPlay() {
actor.act();
}
}
public class Transmogrify {
public static void main(String[] args) {
Stage stage = new Stage();
stage.performPlay();
stage.change();
stage.performPlay();
}
}
// HappyActor
// SadActorCopy the code
We gain dynamic flexibility (also known as “state mode”) at run time by rebinding references to different objects at run time.
Inheritance represents differences between behaviors, and fields represent changes in state.
Pure inheritance and extension
- Is-a relationship (pure inheritance) : Overrides only methods already in the base class, without extending them
- The exported class and the base class have exactly the same interface.
- You just transition up from the exported class, and you never need to know the exact type of object you’re dealing with
- Is-is -like- A relationship: Extends the base class
- The extended parts of the exported class interface are not accessible by the base class.
Downscaling and runtime type recognition
In Java, all transitions are checked. Even if we do a normal parenthetical type conversion, it will still be checked at runtime and return a ClassCastException if it is not the type we want to cast.
practice
Exercise 1
package com.company.ch08;
public class Cycle {
void run() {
System.out.println("Cycle run");
}
}
class Unicycle extends Cycle {
@Override
void run() {
System.out.println("Unicycle run");
}
}
class Bicycle extends Cycle {
@Override
void run() {
System.out.println("Bicycle run");
}
}
class Tricycle extends Cycle {
@Override
void run() {
System.out.println("Tricycle run");
}
}
class Test {
static void ride(Cycle c) {
c.run();
}
public static void main(String[] args) {
Unicycle unicycle = new Unicycle();
Bicycle bicycle = new Bicycle();
Tricycle tricycle = new Tricycle();
unicycle.run();
bicycle.run();
tricycle.run();
}
}
// Unicycle run
// Bicycle run
// Tricycle runCopy the code
Ex 2
package com.company.ch08;
public class Shape {
public void draw() {}
public void erase() {}
}Copy the code
package com.company.ch08; public class Circle extends Shape{ @Override public void draw() { System.out.println("Circle draw"); } @Override public void erase() { System.out.println("Circle erase"); }}Copy the code
package com.company.ch08; public class Square extends Shape{ @Override public void draw() { System.out.println("Square draw"); } @Override public void erase() { System.out.println("Square erase"); }}Copy the code
package com.company.ch08; public class Triangle extends Shape { @Override public void draw() { System.out.println("Triangle draw"); } @Override public void erase() { System.out.println("Triangle erase"); }}Copy the code
package com.company.ch08; import java.util.Random; Public class RandomShapeGenerator {private Random Random = new Random(47); public Shape next() { switch (random.nextInt(3)) { default: case 0: return new Circle(); case 1: return new Square(); case 2: return new Triangle(); }}}Copy the code
package com.company.ch08; public class Shapes { private static RandomShapeGenerator randomShapeGenerator = new RandomShapeGenerator(); public static void main(String[] args) { Shape[] shapes = new Shape[9]; for (int i = 0; i < 9; i++) { shapes[i] = randomShapeGenerator.next(); } for (Shape shape: shapes) { shape.draw(); } } } // Triangle draw // Triangle draw // Square draw // Triangle draw // Square draw // Triangle draw // Square draw // Triangle draw // Circle drawCopy the code
Practice 3
public class Shape {
public void draw() {}
public void erase() {}
public void info() {
System.out.println("Shape info");
}
}Copy the code
Even if the exported class does not override it, the exported class still has the method for inheritance reasons.
package com.company.ch08; public class Circle extends Shape{ @Override public void draw() { System.out.println("Circle draw"); } @Override public void erase() { System.out.println("Circle erase"); } @Override public void info() { System.out.println("Circle info"); }}Copy the code
package com.company.ch08; public class Shapes { private static RandomShapeGenerator randomShapeGenerator = new RandomShapeGenerator(); public static void main(String[] args) { Shape[] shapes = new Shape[9]; for (int i = 0; i < 9; i++) { shapes[i] = randomShapeGenerator.next(); } for (Shape shape: shapes) { shape.info(); } } } // Shape info // Shape info // Circle info // Circle info // Shape info // Shape info // Shape info // Shape info // Circle infoCopy the code
If only one derived class Circle overrides the method, the overridden method will only be called when a Shape of formal type Circle calls info, and the rest will be calls to the base class methods.
Exercise 4
class Line extends Shape { @Override public void draw() { System.out.println("Line draw"); } @Override public void erase() { System.out.println("Line erase"); } } public class Shapes { private static RandomShapeGenerator randomShapeGenerator = new RandomShapeGenerator(); public static void main(String[] args) { Shape[] shapes = new Shape[9]; for (int i = 0; i < 9; i++) { shapes[i] = randomShapeGenerator.next(); } for (Shape shape: shapes) { shape.draw(); } shapes[0] = new Line(); shapes[0].draw(); } } //Shape info //Shape info //Circle info //Circle info //Shape info //Shape info //Shape info //Shape info //Circle infoCopy the code
Practice 5
package com.company.ch08;
public class Cycle {
void run() {
System.out.println("Cycle run");
}
int wheels() {
return 0;
}
}
class Unicycle extends Cycle {
@Override
void run() {
System.out.println("Unicycle run");
}
@Override
int wheels() {
return 1;
}
}
class Bicycle extends Cycle {
@Override
void run() {
System.out.println("Bicycle run");
}
@Override
int wheels() {
return 2;
}
}
class Tricycle extends Cycle {
@Override
void run() {
System.out.println("Tricycle run");
}
@Override
int wheels() {
return 3;
}
}
class Test {
static void ride(Cycle c) {
c.run();
}
public static void main(String[] args) {
Unicycle unicycle = new Unicycle();
Bicycle bicycle = new Bicycle();
Tricycle tricycle = new Tricycle();
unicycle.run();
bicycle.run();
tricycle.run();
Cycle[] cycles = new Cycle[]{unicycle, bicycle, tricycle};
for (Cycle cycle: cycles) {
System.out.println("cycle.wheels() = " + cycle.wheels());
}
}
}
// Unicycle run
// Bicycle run
// Tricycle run
// cycle.wheels() = 1
// cycle.wheels() = 2
// cycle.wheels() = 3Copy the code
Exercise 6
package com.company.ch08;
enum Note {
MIDDLE_C, C_SHARP, B_FLAT;
}
class Instrument {
void play(Note n) {
System.out.println("Instrument.play() n = " + n);
}
@Override
public String toString() {
return "Instrument";
}
void adjust() {
System.out.println("Adusting Instrument");
}
}
class Wind extends Instrument {
@Override
void play(Note n) {
System.out.println("Wind.play() n = " + n);
}
}
class Percussion extends Instrument {
@Override
void play(Note n) {
System.out.println("Percussion.play() n = " + n);
}
@Override
public String toString() {
return "Percussion";
}
@Override
void adjust() {
System.out.println("Adjusting Percussion");
}
}
class Stringed extends Instrument {
@Override
void play(Note n) {
System.out.println("Stringed.play() n = " + n);
}
@Override
public String toString() {
return "Stringed";
}
@Override
void adjust() {
System.out.println("Adjusting Stringed");
}
}
class Brass extends Wind {
@Override
void play(Note n) {
System.out.println("Brass.play() n = " + n);
}
@Override
void adjust() {
System.out.println("Adjusting Brass");
}
}
class Woodwind extends Wind {
@Override
void play(Note n) {
System.out.println("Woodwind.play() n = " + n);
}
@Override
public String toString() {
return "Woodwind";
}
}
public class Music3 {
public static void tune(Instrument i) {
i.play(Note.MIDDLE_C);
}
public static void tuneAll(Instrument[] instruments) {
for (Instrument instrument: instruments) {
tune(instrument);
}
}
public static void main(String[] args) {
Instrument[] instruments = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind(),
};
tuneAll(instruments);
for (Instrument instrument: instruments) {
System.out.println(instrument);
}
}
}
// Wind.play() n = MIDDLE_C
// Percussion.play() n = MIDDLE_C
// Stringed.play() n = MIDDLE_C
// Brass.play() n = MIDDLE_C
// Woodwind.play() n = MIDDLE_C
// Instrument
// Percussion
// Stringed
// Instrument
// WoodwindCopy the code
Exercise 7
class Piano extends Instrument {
@Override
void play(Note n) {
System.out.println("Piano.play() n = " + n);
}
@Override
public String toString() {
return "Piano";
}
@Override
void adjust() {
System.out.println("Adjusting Piano");
}
}
public class Music3 {
public static void tune(Instrument i) {
i.play(Note.MIDDLE_C);
}
public static void tuneAll(Instrument[] instruments) {
for (Instrument instrument: instruments) {
tune(instrument);
}
}
public static void main(String[] args) {
Instrument[] instruments = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind(),
new Piano(),
};
tuneAll(instruments);
for (Instrument instrument: instruments) {
System.out.println(instrument);
}
}
}
// Wind.play() n = MIDDLE_C
// Percussion.play() n = MIDDLE_C
// Stringed.play() n = MIDDLE_C
// Brass.play() n = MIDDLE_C
// Woodwind.play() n = MIDDLE_C
// Piano.play() n = MIDDLE_C
// Instrument
// Percussion
// Stringed
// Instrument
// Woodwind
// PianoCopy the code
8
class InstrumentGenerator { private Random random = new Random(42); public Instrument next() { switch (random.nextInt(6)) { default: case 0: return new Wind(); case 1: return new Percussion(); case 2: return new Stringed(); case 3: return new Brass(); case 4: return new Woodwind(); case 5: return new Piano(); } } } public class Music3 { public static void tune(Instrument i) { i.play(Note.MIDDLE_C); } public static void tuneAll(Instrument[] instruments) { for (Instrument instrument: instruments) { tune(instrument); } } public static void main(String[] args) { Instrument[] instruments = new Instrument[10]; InstrumentGenerator instrumentGenerator = new InstrumentGenerator(); for (int i = 0; i < 10; i++) { instruments[i] = instrumentGenerator.next(); } tuneAll(instruments); for (Instrument instrument: instruments) { System.out.println(instrument); } } } // Stringed.play() n = MIDDLE_C // Brass.play() n = MIDDLE_C // Wind.play() n = MIDDLE_C // Stringed.play() n = MIDDLE_C // Wind.play() n = MIDDLE_C // Percussion.play() n = MIDDLE_C // Piano.play() n = MIDDLE_C // Stringed.play() n = MIDDLE_C // Percussion.play() n = MIDDLE_C // Piano.play() n = MIDDLE_C // Stringed // Instrument // Instrument // Stringed // Instrument // Percussion // Piano // Stringed // Percussion // PianoCopy the code
Practice 9
package com.company.ch08;
public class Rodent {
void eat() {
System.out.println("Rodent.eat()");
}
public static void main(String[] args) {
Rodent[] rodents = new Rodent[] {
new Rodent(),
new Mouse(),
new Gerbil(),
new Hamster(),
};
for (Rodent rodent: rodents) {
rodent.eat();
}
}
}
class Mouse extends Rodent {
@Override
void eat() {
System.out.println("Mouse.eat()");
}
}
class Gerbil extends Rodent {
@Override
void eat() {
System.out.println("Gerbil.eat()");
}
}
class Hamster extends Rodent {
@Override
void eat() {
System.out.println("Hamster.eat()");
}
}
// Rodent.eat()
// Mouse.eat()
// Gerbil.eat()
// Hamster.eat()Copy the code
Practice 10
package com.company.ch08;
class Base {
void func1() {
func2();
}
void func2() {
System.out.println("Base");
}
}
public class Ex10 extends Base {
@Override
void func2() {
System.out.println("Ex10");
}
public static void main(String[] args) {
Base base = new Ex10();
base.func1();
}
}
// Ex10Copy the code
Because func2 is neither static nor final, it is dynamically bound, so the call to func2 in the base class func1 is also called to func2 in the derived class.
Practice 11
package com.company.ch08; class Meal { Meal() { System.out.println("Meal()"); } } class Bread { Bread() { System.out.println("Bread()"); } } class Cheese { Cheese() { System.out.println("Cheese()"); } } class Lettuce { Lettuce() { System.out.println("Lettuce()"); } } class Lunch extends Meal { Lunch() { System.out.println("Lunch()"); } } class PortableLunch extends Lunch { PortableLunch() { System.out.println("PortableLunch()"); } } class Pickle { Pickle() { System.out.println("Pickle"); } } public class Sandwich extends PortableLunch { private Bread b = new Bread(); private Cheese c = new Cheese(); private Lettuce l = new Lettuce(); private Pickle p = new Pickle(); public Sandwich() { System.out.println("Sandwich()"); } public static void main(String[] args) { new Sandwich(); }}Copy the code
Exercise 12
package com.company.ch08; public class Rodent { Rodent() { System.out.println("Rodent"); } void eat() { System.out.println("Rodent.eat()"); } public static void main(String[] args) { Rodent[] rodents = new Rodent[] { new Rodent(), new Mouse(), new Gerbil(), new Hamster(), }; for (Rodent rodent: rodents) { rodent.eat(); } } } class Mouse extends Rodent { Mouse() { System.out.println("Mouse"); } @Override void eat() { System.out.println("Mouse.eat()"); } } class Gerbil extends Rodent { Gerbil() { System.out.println("Gerbil"); } @Override void eat() { System.out.println("Gerbil.eat()"); } } class Hamster extends Rodent { Hamster() { System.out.println("Hamster"); } @Override void eat() { System.out.println("Hamster.eat()"); }}Copy the code
Practice of 13
package com.company.ch08; class Shared { private int refcount = 0; private static long counter = 0; private final long id = counter++; public Shared() { System.out.println("Create " + this); } void addRef() { refcount++; } protected void dispose() { if(--refcount == 0) System.out.println("Disposing " + this); } @Override public String toString() { return "Shared{" + "id=" + id + '}'; } @Override protected void finalize() throws Throwable { System.out.println("finalize()"); if (refcount ! = 0) { System.out.println("refcount ! = 0 "); } super.finalize(); } } class Composing { private Shared shared; private static long counter = 0; private final long id = counter++; public Composing(Shared shared) { System.out.println("Creating " + this); this.shared = shared; this.shared.addRef(); } protected void dispose() { System.out.println("disposing " + this); shared.dispose(); } @Override public String toString() { return "Composing{" + "id=" + id + '}'; } } public class ReferenceCounting { public static void main(String[] args) { // Shared shared = new Shared(); // Composing[] composings = { // new Composing(shared), new Composing(shared), // new Composing(shared), new Composing(shared), // new Composing(shared) // }; // // for (Composing composing: composings) { // composing.dispose(); // } new Composing(new Shared()); System.gc(); }}Copy the code
Practice 14
Practice of 15
package com.company.ch08;
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);
}
@Override
void draw() {
System.out.println("RoundGlyph.draw(), radius = " + radius);
}
}
class RectangularGlygh extends Glyph {
private int length;
RectangularGlygh(int length) {
this.length = length;
System.out.println("RectanguarGlygh length = " + length);
}
@Override
void draw() {
System.out.println("RectanguarGlygh.draw() length = " + length);
}
}
public class PolyConstructors {
public static void main(String[] args) {
new RectangularGlygh(10);
}
}
// Glyph() before draw()
// RectanguarGlygh.draw() length = 0
// Glyph() after draw()
// RectanguarGlygh length = 10Copy the code
Practice 16
package com.company.ch08;
class Status {
void func() {}
}
class StatusA extends Status {
void func() {
System.out.println("Status A");
}
}
class StatusB extends Status {
void func() {
System.out.println("Status B");
}
}
class StatusC extends Status {
void func() {
System.out.println("Status C");
}
}
class AlterStatus {
Status status = new StatusA();
void A() {
status = new StatusA();
}
void B() {
status = new StatusB();
}
void C() {
status = new StatusC();
}
void call() {
status.func();
}
}
public class Starship {
public static void main(String[] args) {
AlterStatus alterStatus = new AlterStatus();
alterStatus.call();
alterStatus.B();
alterStatus.call();
alterStatus.C();
alterStatus.call();
alterStatus.A();
alterStatus.call();
}
}
// Status A
// Status B
// Status C
// Status ACopy the code
Practice 17
package com.company.ch08; public class Cycle { void run() { System.out.println("Cycle run"); } int wheels() { return 0; } } class Unicycle extends Cycle { @Override void run() { System.out.println("Unicycle run"); } @Override int wheels() { return 1; } void balance() {} } class Bicycle extends Cycle { @Override void run() { System.out.println("Bicycle run"); } @Override int wheels() { return 2; } void balance() {} } class Tricycle extends Cycle { @Override void run() { System.out.println("Tricycle run"); } @Override int wheels() { return 3; } } class Test { static void ride(Cycle c) { c.run(); } public static void main(String[] args) { Cycle[] cycles = new Cycle[]{new Unicycle(), new Bicycle(), new Tricycle()}; // for(Cycle cycle: cycles) { // cycle.balance(); Unicycle Unicycle = (Unicycle)cycles[0]; Bicycle bicycle = (Bicycle)cycles[1]; Tricycle tricycle = (Tricycle)cycles[2]; unicycle.balance(); bicycle.balance(); // tricycle.balance(); // Cannot call}}Copy the code
This article was originally published by Code & Fun