Polymorphism
- From another perspective, the interface is separated from the specific implementation details, and the separation of "what" and "how to do" is realized.
- Polymorphism: the same type shows different behaviors.
1. Upward transformation
The act of obtaining an object handle and using it as a parent handle is called upward transformation
//Music.java //Inheritance & upcasting class Note{ private int value; Note(int val) { value = val; } public static final Note middleC=new Note(0), cSharp=new Note(1), cFlat = new Note(2); } //etc class Instrument{ public void play(Note n){ System.out.println("Instrument.play"); } } //Wind object are instrument because they have same interface class Wind extends Instrument{ //Redefine interface method: public void play(Note n) { System.out.println("Wind.play()"); } } public class Music { public static void tune(Instrument i){ //.... i.play(Note.middleC); } public static void main(String[] args) { Wind flute=new Wind(); tune(flute); //upcasting } }
Music.tune() receives an Instrument handle and everything derived from the Instrument
·Why upward transformation
- The upward transformation can save a lot of code
//Music2.java //Overloading instead of upcasting class Note2 { public static final Note2 middleC = new Note2(0), cSharp = new Note2(1), cFlat = new Note2(2); private int value; private Note2(int val) { value = val; } } //Etc. class Instrument2{ public void play(Note2 n) { System.out.println("Instrument2.play()"); } } class Wind2 extends Instrument2 { @Override public void play(Note2 n) { System.out.println("Wind2.play()"); } } class Stringed2 extends Instrument2 { @Override public void play(Note2 n) { System.out.println("Stringed.play()"); } } class Brass2 extends Instrument2 { @Override public void play(Note2 n) { System.out.println("Brass2.play()"); } } public class Music2 { public static void tune(Wind2 i) { i.play(Note2.middleC); } public static void tune(Stringed2 i) { i.play(Note2.middleC); } public static void tune(Brass2 i) { i.play(Note2.middleC); } public static void main(String[] args) { Wind2 flute =new Wind2(); Stringed2 violin = new Stringed2(); Brass2 frenchHorn = new Brass2(); tune(flute); //No upcasting tune(violin); tune(frenchHorn); } }
Like the above code, you can avoid upward transformation, but add a lot of code. You need to rewrite the play() and tune() methods for each type. At the same time, if you forget to rewrite a method, the compiler will not report an error. In this way, the whole operation process of the type is extremely difficult to manage and is in danger of getting out of control.
But wouldn't it be much simpler to write only one method and use the base class as an argument or parameter instead of using those specific derived classes?
In other words, if we can ignore the derived classes and only let our code deal with the basic classes, the workload saved will be difficult to estimate
of
2. In depth understanding (binding)
Watch music tune() in Java:
public static void tune(Instrument i){ //... i.play(Note.middleC); }
It accepts and receives an Instrument handle, so in this case, the compiler knows that the Instrument handle points to a Wind, not a Brass or Stringed. The compiler has no way to know, so it is necessary to discuss Binding next
Binding of method call
- Binding: connecting a method call and a method body together
- Static binding: binding is executed before the program runs, which is also called early binding
- Dynamic binding: binding is performed during runtime based on the type of object, also known as late binding.
If a language implements dynamic binding, at the same time, it must provide some mechanisms to judge the type of the object during operation and call appropriate methods respectively, that is, the compiler still does not know the type of the object at this time, but the method call mechanism can investigate and find the correct method body by itself
All methods bound by java adopt dynamic binding unless the method is declared final. This means that we don't have to decide whether to bind dynamically -- it happens automatically.
Final method can prevent others from overwriting that method, but more importantly, it can effectively "close" dynamic binding, so that the compiler can generate more efficient code for final method calls.
How to produce correct behavior
All methods in Java are polymorphic through dynamic binding
For example, the parent class Shape has a large number of subclasses: Circle, Square, Triangle, etc
Upward transformation can be realized through Shape s=new Cirbcle(); Simply show it
Here we create a Cirbcle object and assign it to the Shape handle (it seems wrong on the surface), but it is actually feasible - because Cirbcle belongs to Shape.
s.draw(); Similarly, people think that Shape's draw () will be called, because it is a Shape() handle after all. But the actual call is Cirbcle's Shape(), because dynamic binding has been involved (polymorphism)
3. Overload and coverage
- Overloading: the same thing has different meanings in different places
- Overriding: there is only one meaning anytime, anywhere, but the original meaning is replaced by the later meaning. (different parameter types and no polymorphism are allowed)
4. Abstract classes and methods
- Abstract method: abstract void X();
- You need to override it in a subclass or declare the subclass as abstract
- Only method declarations, no method implementations (created in abstract classes)
- static and private decoration cannot be used
- Abstract class: abstract class J {}:
- A class containing one or more abstract methods must be declared abstract
- Only declare the existence of the method, not the implemented class
- Cannot be instantiated (cannot be created object)
Creating abstract classes and abstract methods is sometimes very useful for us. It can become an obvious fact that the abstraction of a method clearly tells users and compilers how to use it
It can be seen that there is no change in the code except
// Music4.java //Abstract classes and methods abstract class Instrument4{ int i; //Storage allocated for each public abstract void play(); //abstract methods public String what(){ return "Instrument4.play()"; } public abstract void adjust(); } class Wind4 extends Instrument4 { @Override public void play() { System.out.println("Wind4.play()"); } @Override public String what() { return "Wind4"; } @Override public void adjust() {} } class Percussion4 extends Instrument4{ @Override public void play() { System.out.println("Percussion4.play()"); } @Override public String what() { return "Percussion4"; } @Override public void adjust() {} } class Stringed4 extends Instrument4{ @Override public void play() { System.out.println("Stringed4.play()"); } @Override public String what() { return "Stringed4"; } @Override public void adjust() {} } class Woodwind4 extends Wind4{ @Override public void play() { System.out.println("Woodwind4.play()"); } @Override public String what() { return "Woodwind4"; } } class Brass4 extends Wind4{ @Override public void play() { System.out.println("Brass4.play()"); } @Override public void adjust() { System.out.println("Brass4.adjust()"); } } public class Music4 { //Doesn't care about type, so new types added to the system still work right: static void tune(Instrument4 i){ i.play(); } static void tuneAll(Instrument4[] e){ for (int i = 0; i < e.length; i++) { tune(e[i]); } } public static void main(String[] args) { Instrument4[] orchetra=new Instrument4[5]; int i=0; //Upcasting during addition to the array: orchetra[i++]=new Wind4(); orchetra[i++]=new Brass4(); orchetra[i++]=new Woodwind4(); orchetra[i++]=new Stringed4(); orchetra[i++]=new Percussion4(); tuneAll(orchetra); } }
5. interface
- Think of it as a "pure" abstract class
- Contains data members of basic data types. The default values are static and final
- All classes that implement me should look like I am now
- Only one form is provided, and implementation details are not provided
- The method in the interface defaults to public
- interface jj{/ / body}; Implementation class class aa implements jj{/ / body}
- Reasons for using the interface:
- Transition up to multiple parent classes
- Prevent client programmers from creating an object of this class
- And stipulate that it is only an interface
How to select interfaces and abstract classes?
- If you want to create any method definitions and member variables for the base class, select the interface
- If you know something becomes a basic class in advance, the first choice is the interface
- Abstract classes are used only when method definitions or member variables must be used
Multiple inheritance of java
- Multiple inheritance: X belongs to a, b and c. the action of merging multiple classes is called multiple inheritance.
- A class can implement multiple interfaces
- Syntax: class X extends a implements b,c {/ / main body} can also not inherit
The following example shows the merging of a "concrete" class with several interfaces, which finally generates a new class:
//Adventure.java //Multiple interfaces interface CanFight { void fight(); } interface CanSwim { void swim(); } interface CnaFly { void fly(); } class ActionCharacter { public void fight() { System.out.println("ActionCharacter.fight"); } } class Hero extends ActionCharacter implements CanFight, CanSwim, CnaFly { @Override public void swim() { System.out.println("HERO.SWIM"); } @Override public void fly() { System.out.println("HERO.FLY"); } } public class Adventure { static void t(CanFight x){x.fight();} static void u(CanSwim x){x.swim();} static void v(CnaFly x){x.fly();} static void w(ActionCharacter x){x.fight();} public static void main(String[] args) { Hero i=new Hero(); //Hero is transformed upward into concrete classes: ActionCharacter, interfaces: canfight, canswing, and cnafly objects t(i); //Treat it as a CanFight v(i); //Treat it as a CnaFly w(i); //Treat it as a ActionCharacter u(i); //Treat it as a CanSwim } }
Extending interfaces through inheritance
- Interfaces can also be inherited (or extended) and can inherit multiple interfaces
- Syntax: interface aa extends interface1,interface2 {/ / body}
- A class can also implement multiple interfaces. class aa implements interface1,interface2 {/ / body}
//HorrorShow.java //Extending an interface with inheritance interface Monster { void menace(); } interface DangerousMonster extends Monster { void destroy(); } interface Lethal { void kill(); } interface Vampire extends DangerousMonster, Lethal { void drinkBlood(); } class DragonZilla implements DangerousMonster,Vampire{ @Override public void menace() { } @Override public void destroy() { } @Override public void kill() { } @Override public void drinkBlood() { } } public class HorrorShow { static void u(Monster b) { b.menace(); } static void v( DangerousMonster d) { d.menace(); d.destroy(); } public static void main(String[] args) { DragonZilla if2=new DragonZilla(); u(if2); v(if2); if2.kill(); } }
Constant grouping
Since all fields placed in the interface automatically have static and final attributes, the interface is a good tool for grouping multi constant values. It has a very similar effect to the enum of C or C + +:
//:Months.java //Using interface to create groups of constants public interface Months { int JANUARY=1, FEBRUARY=2, MARCH=3, APRIL=4, MAY=5, JUNE=6, JULY=7, AUGUST=8, SEPTEMBER=9, OCTOBER=10, NOVEMBER=11, DECEMBER=12; } ///:~
// Month2.java //A more robust enumeration system public final class Month2 { private String name; private Month2(String nm) { name = nm; } public String otString() { return name; } public final static Month2 JAN = new Month2("January"), FEB = new Month2("February"), MAR = new Month2("March"), APR = new Month2("April"), MAY = new Month2("May"), JUN = new Month2("June"), JUL = new Month2("July"), AUG = new Month2("August"), SEP = new Month2("September"), OCT = new Month2("October"), NOV = new Month2("November"), DEC = new Month2("December"); public final static Month2[] month ={ JAN, JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC }; public static void main(String[] args) { Month2 m= Month2.JAN; System.out.println(m.otString()); m=Month2.month[12]; System.out.println(m.otString()); System.out.println(m == Month2.DEC); System.out.println(m.equals(Month2.DEC)); } }
Initialize fields in the interface
The fields defined in the interface automatically have static and final attributes. They cannot be "blank final", but can be initialized to non constant expressions
Type. For example:
//RandVla.java //Initializing interface fields with //non-constant initializers public interface RandVal { int rint=(int) (Math.random()*10); long rlong = (long) (Math.random()*10); float rfloat=(float) (Math.random()*10); double rdouble=Math.random()*10; }
Because fields are static, they are initialized after the first class load and before any fields are accessed for the first time. Here is a
Simple test:
//TestRandVals.java public class TestRandVals { public static void main(String[] args) { System.out.println(RandVal.rfloat); System.out.println(RandVal.rlong); System.out.println(RandVal.rfloat); System.out.println(RandVal.rdouble); } }
Of course, fields are not part of an interface, but are stored in the static storage area of that interface.
6. Internal class
- Inner class: putting one class into another is called inner class.
import ln.Const; import java.security.DomainCombiner; //Parcel1.java //Creating inner classes public class Parcel { class Contents { private int i = 11; public int value() { return i; } } class Destination { private String label; Destination(String whereTO) { label = whereTO; } String readLabel(){ return label; } //Using inner classes looks just like //using any other class,within Parcel1: } public void ship(String dest){ Contents c=new Contents(); Destination d=new Destination(dest); } public static void main(String[] args) { Parcel parcel=new Parcel(); parcel.ship("Tanzania"); } }
The use of inner classes seems to be no different from any other class. The only obvious difference here is its
The name is nested inside Parcel1. But you will soon know that this is not the only difference.
More typically, an external class has a special method that returns a handle to an internal class. As follows:
//Parcel2.java //Returning a handle to an inner class public class Parcel2 { class Content2{ private int i=11; public int value(){ return i; } } class Destination2{ private String label; Destination2(String whereTo){ label=whereTo; } String readLabel(){ return label; } } public Destination2 to(String s){ return new Destination2(s); } public Content2 cont(){ return new Content2(); } public void ship(String dest){ Content2 c=cont(); Destination2 d=to(dest); } public static void main(String[] args) { Parcel2 p=new Parcel2(); p.ship("Tanzania"); Parcel2 q=new Parcel2(); //Defining handles to inner classes: Parcel2.Content2 c=q.cont(); Parcel2.Destination2 d=p.to("Borneo"); } }///:~
Internal classes and upward transformation
Ordinary (Non internal) classes cannot be set to private or protected -- only public or "friendly" classes are allowed.
Hide the details of the implementation through internal classes
import ln.Const; //Parcel3.java //Returning a handle to an inner class abstract class Contents{ abstract public int value(); } interface Destination{ String readLabel(); } public class Parcel3 { private class PContents extends Contents{ private int i=11; public int value() { return i; } } protected class PDestination implements Destination{ private String label; private PDestination(String whereTo){ label=whereTo; } public String readLabel(){return label;} } public Destination dest(String s) { return new PDestination(s); } public Contents cont() { return new PContents(); } } class test{ public static void main(String[] args) { Parcel3 p=new Parcel3(); Contents c=p.cont(); Destination d=p.dest("Tanzania"); //Illegal -- can't access private class: //! Parcel3.PContents c = p.new PContents(); } }
Internal classes in methods and scopes
Internal classes can be created in the method or scope for the following two reasons:
- Implement some form of interface, and you can create and return a handle yourself.
- To solve a complex problem, I want to create a class to assist my own program scheme, and I am not willing to expose it.
Here are some examples:
The first is three interfaces:
//:Contents.java package Package.innerscopes; public interface Contents { int value(); }
//:Destination.java package Package.innerscopes; public interface Destination { String readLabel(); }///:~
//:Wrapping.java package Package.innerscopes; public class Wrapping { private int i; public Wrapping(int c) { i=c; } public int value(){ return i; } }
Example 1 nested inner classes in the scope of a method
//:Parcel4.java //Nesting a class within a method package Package.innerscopes; public class Parcel4 { public Destination dest(String s) { class PDestination implements Destination { private String label; private String whereTO; private PDestination(String whereTO) { label = whereTO; } @Override public String readLabel() { return label; } } return new PDestination(s); } public static void main(String[] args){ Parcel4 p=new Parcel4(); Destination d=p.dest("Tanzania"); } }///:`
Example 2 nesting inner classes in any scope
//:Parcel5.java //Nesting a class within a scope package Package.innerscopes; public class Parcel5 { private void internalTracking(boolean b) { if (b) { class TrackingSlip{ private String id; TrackingSlip(String s){ id=s; } String getSlip(){return id;} } TrackingSlip ts=new TrackingSlip("slip"); String s =ts.getSlip(); } //Can'use it here! out of scope: //!TrackingSlip ts=new TrackingSlip("s"); } public void track(){internalTracking(true);} public static void main(String[] args) { Parcel5 p=new Parcel5(); p.track(); } }
Example 3: a method whose return value is an anonymous inner class
package Package.innerscopes; //Parcel6.java //A method that returns an anonymous inner class public class Parcel6 { public Contents cont(){ return new Contents() { //Anonymous internal private int i=11; @Override public int value() { return i; } }; //Semicolon required in this case } public static void main(String[] args) { Parcel6 p=new Parcel6(); Contents c=p.cont(); } }
The anonymous inner class is equivalent to
class MyContents extends Contents { private int i = 11; public int value() { return i; } } return new MyContents();
Example 4 anonymous inner class calling base class constructor
//Parcel7.java package Package.innerscopes; //An anonymous inner class that calls the //base-class constructor public class Parcel7 { //Base constructor call: public Wrapping wrap(int x){ //Base constructor call: return new Wrapping(x){ public int value(){ return super.value()*47; } }; //Semicolon required } public static void main(String[] args){ Parcel7 p=new Parcel7(); Wrapping w=p.wrap(10); System.out.println(w.value()); } }
Example 5 initializing a field in an anonymous inner class
//:Parcel8,java //An anonymous inner class that performs //initialization.A briefer version package Package.innerscopes; public class Parcel8 { //Argument must be final to use inside //anonymous inner class: public Destination dest(final String dest){ return new Destination() { private String label=dest; @Override public String readLabel() { return label; } }; } public static void main(String[] args) { Parcel8 p =new Parcel8(); Destination d=p.dest("tanzania"); System.out.println(d.readLabel()); } }
Example 6: an anonymous class is built through instance initialization (anonymous inner classes cannot have Builders)
//Parcel9.java //Using "instance initialization" to perform //construction on an anonymous inner class package Package.innerscopes; public class Parcel9 { public Destination dest(final String dest,final float price){ return new Destination() { private int cost; //instance initialization for each object: { //Instance initialization module -- anonymous inner class builder cost=Math.round(price); if (cost>100) System.out.println("Over budget!"+cost); } private String label=dest; @Override public String readLabel() { return label; } }; } public static void main(String[] args) { Parcel9 p=new Parcel9(); Destination d=p.dest("Tanzania",101.485F); } }
Link to external class
- When creating an inner class, the object of the inner class also has a link to the encapsulated object, so it can access the members of the encapsulated object - without any qualification
- The inner class has access to all elements of the encapsulated class (outer class)
//Sequence.java //Holds a sequence of Object interface Selector{ boolean end (); Object current(); void next(); } public class Sequence { private Object[] o; private int next =0; public Sequence(int size){ o=new Object[size]; } public void add(Object x){ if (next <o.length){ o[next]=x; next++; } } private class SSelector implements Selector{ int i =0; public boolean end(){ return i==o.length; } public Object current(){ return o[i]; } public void next(){ if (i < o.length) i++; } } public Selector getSelector(){ return new SSelector(); } public static void main(String[] args) { Sequence s=new Sequence(10); for(int i =0; i<10;i++){ s.add(Integer.toString(i)); } Selector sl=s.getSelector(); while (!sl.end()) { System.out.println((String)sl.current()); sl.next(); } } }
static inner class
By default, an object of an inner class holds a handle to an object of the enclosing class that created it
The static inner class means:
- To create a static inner class object, we do not need an outer class object
- An external class object cannot be accessed from an object of a static internal class
However, there are some limitations: because static members can only be located at the external level of a class, internal classes cannot have static data or
static inner class.
//Parcel10.java //static inner classes abstract class Contents1{ abstract public int value(); } interface Destination1{ String readLabel(); } public class Parcel10 { private static class PContents extends Contents1 { private int i=11; public int value(){return i;} } protected static class PDestination implements Destination1 { private String label; private PDestination(String whereTo){ label =whereTo; } public String readLabel(){return label;} } public static Destination1 dest(String s){ return new PDestination(s); } public static Contents1 cont(){ return new PContents(); } public static void main(String[] args) { Contents1 c = cont(); /* Parcel10 p=new Parcel10(); PDestination ddd=new PDestination("SS"); */ Destination1 d = dest("Tanzania"); } }
Usually, we don't set any code in an interface, but static internal classes can become part of the interface. Because classes are "static,",
Therefore, it will not violate the rules of the interface - the static internal class is only located inside the namespace of the interface:
//:IInterface.java //Static inner class inside interface interface IInterface { static class Inner{ int i,j,k; public Inner(){ System.out.println("ss"); } void f(){} } }
Reference external class object
//:Creating inner classes public class Parcel11 { class Contents{ private int i=11; public int value(){return i;} } class Destination{ private String label; Destination(String whereTo) { label=whereTo; } String readLabel(){return label;} } public static void main(String[] args) { Parcel11 p=new Parcel11(); //Must use instance of outer class //to create an instances of inner class Parcel11.Contents c =p.new Contents(); //Reference external class Parcel11.Destination d=p.new Destination("Tanzania"); } }
Inherit from inner class
As you can see, InheritInner only extends the inner class, not the outer class. But when you need to create a builder,
The default object has no meaning. We can't just pass a handle to the encapsulated object. In addition, the following syntax must be used in the Builder:
enclosingClassHandle.super();
It provides the necessary handles for the program to compile correctly.
//: InheritInner.java //Inheriting an inner class class WithInner{ class Inner{} } public class InheritInner extends WithInner.Inner { //!InheritInner(){} //Won't compile InheritInner(WithInner wi) { wi.super(); } public static void main(String[] args) { WithInner wi=new WithInner(); InheritInner inheritInner=new InheritInner(wi); } }
Can internal classes be overridden?
What happens if you create an inner class, then inherit from the encapsulated class, and redefine the inner class? In other words, we can
Can you override an inner class? This may seem like a very useful concept, but "overriding" an inner class -- as if it were an outer class
Another approach - this concept can't actually do anything:
An inner class cannot be overridden like a method
//:BigEgg.java //An inner class cannot be overriden like a method class Egg{ protected class Yolk{ public Yolk(){ System.out.println("Egg.Yolk()"); } } private Yolk y; public Egg(){ System.out.println("new Egg()"); y=new Yolk(); } } public class BigEgg extends Egg{ public class Yolk{ public Yolk(){ System.out.println("BigEgg.Yolk()"); } } public static void main(String[] args){ new BigEgg(); } }
Override an inner class correctly
import com.fr.web.core.A.Y; //:BigEgg.java //Proper inheritance of an inner class class Egg2 { protected class Yolk{ public Yolk(){ System.out.println("Egg2.Yolk()"); } public void f(){ System.out.println("Egg2.f()"); } } private Yolk y=new Yolk(); public Egg2(){ System.out.println("new Egg()"); } public void insertYolk(Yolk yy){y=yy;} public void g(){y.f();} } public class BigEgg2 extends Egg2{ public class Yolk extends Egg2.Yolk{ public Yolk(){ System.out.println("BigEdd2.Yolk()"); } public void f(){ System.out.println("BigEgg2.Yolk.f()"); } } public BigEgg2(){ insertYolk(new Yolk()); } public static void main(String[] args) { Egg2 e2=new BigEgg2(); e2.g(); } }
Internal class identifier
Each class generates one Class file is used to contain all information related to how to create objects of this type (this information generates a metaclass named class object), so the internal class must also generate the corresponding. Class file. The name of this file follows the following format: first, encapsulate the name of the class+ + within Department class name word . example as : W i t h I n n e r +Internal class name. For example: WithInner +Internal class name. For example: withinnerinner class
Why use inner classes—— Control framework
7. Builder and polymorphism
Builders are not polymorphic, but it is still necessary to understand how builders are used in complex hierarchies and with polymorphism.
Call order of builder
The special task of the Builder: check whether the object has been built correctly.
Call order of Builder:
- The storage space allocated for the object is initialized to binary zeros before any other action is taken.
- Call the base class builder. This step will be repeated. First, the root of the hierarchy will be built, then the next derived class will be built, and so on. Until the highest level of derived classes.
- Then call the member initialization module in the declared order
- Call the body of the derived builder.
Vernacular: first call the ancestor class builder, then the builder of grandfather class, parent class, grandson class, etc. after completion, call the initialization module in the order of declaration, and finally call the body of the derived Builder (that is, the builder of this class)
Why?
During inheritance, the derived class can access any public and protected members of the base class, which means that when we derive a class, all members of the base class are valid, which must be guaranteed within the builder
All members used have been built. The only way to achieve this is to call the base class builder first. Then enter the derived class constructor
After the builder is built, all members that we can access in the basic class have been initialized. In addition, all member objects (that is, set by the composition method)
When defining objects within a class (such as b, c and l in the above example), we should initialize them as much as possible
Therefore, you should also ensure that all members within the builder are valid. If we stick to this rule, it will help us identify all basic classes
The member and the member object of the current object have been properly initialized. Unfortunately, this approach does not apply to all situations, which will be
The next section specifies.
//:Sandwich.java //Order of constructor calls class Meal{ Meal(){ System.out.println("Meal()"); } } class Bread{ Bread(){ System.out.println("Brad()"); } } 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()"); } } public class Sandwich extends PortableLunch { Sandwich(){ System.out.println("Sandwich()"); } Bread b=new Bread(); Cheese c=new Cheese(); Lettuce l=new Lettuce(); public static void main(String[] args) { new Sandwich(); } }
Inherit and finalize() (skip, don't understand)
Behavior of polymorphic methods inside the builder (skipped, not understood)
If possible, avoid calling any methods in the builder
8. Design through inheritance
Inheritance is used to express the differences between behaviors, and member variables are used to express the changes of state
/** * :Transmogrify.java * Dynamically changing the behavior of * an object via composition */ interface Actor1{ void act(); } class HappyActor1 implements Actor{ @Override public void act() { System.out.println("HappyActor"); } } class SadActor1 implements Actor { @Override public void act() { System.out.println("SadAction"); } } class Stage1{ Actor a= new HappyActor1(); /** * SadActor1 Point to handle A */ void change(){ a=new SadActor1(); } void go(){ a.act(); } } public class Transmogrify1 { public static void main(String[] args) { Stage1 s=new Stage1(); s.go(); //Prints "HappyActor" s.change(); s.go();//Print "SadActor1" } }
Pure inheritance and extensions
There are two methods void f() void g() in the basic class Useful, which are covered in its derived class, and three methods u, v and w are added. This is like extending the user interface. Although it is a wise move, it has one disadvantage: = = the extended part in the interface cannot be used in the basic class, so once it is transformed upward, it cannot call the new method of the extended part== If upward transformation is not used at this time, such problems will not occur.
Downward transition and runtime type identification
/** * :RTTI.java * DownCasting & Run_Time Type Identification(RTTI) * Identification during downward transformation and operation */ class useful { public void f() { } public void g() { } } class MoreUseful extends useful { @Override public void f() { } @Override public void g() { } public void u() { } public void v() { } public void w() { } } public class RTTI { public static void main(String[] args) { useful[] x = { new useful(), new MoreUseful() }; x[0].f(); x[1].g(); //Compile-time:method not found in useful: //!x[1].u(); ((MoreUseful)x[1]).u(); // Downcast and RTTI ((MoreUseful)x[0]).u(); //Exception thrown useful usef=new useful(); ((MoreUseful)usef).u(); //The basic class useful is transformed downward to the derived class MoreUseful } }