//Chapter 10 // Polymorphism works off superclass references. Single variable can // refer to objects from different classes. // Allows program to process objects of class hierarchy as if they were // objects of the superclass. // Subclass object is-a object of superclass and can be treated as such. // Send the same message to objects from different classes. // Polymorphism allows additional types to be created and added to system // without modification of the system. New types that respond to existing // method calls can be incorporated into the system without modifying // the base system. It promotes extensibility. // You can deal in generalities and let the execution-time environment // handle the specifics. Can command objects to behave in manners // appropriate to those objects, without knowing the types of the objects. public class Polymorphism { public static void main( String[] args ) { X x1 = new X(); Y y1 = new Y(); System.out.print( "x1.m1Inherited(): " ); x1.m1Inherited(); System.out.print( "x1.m3Overridden(): " ); x1.m3Overridden(); System.out.print( "y1.m1Inherited(): " ); y1.m1Inherited(); System.out.print( "y1.m3Overridden(): " ); y1.m3Overridden(); System.out.print( "y1.m2Subclass(): " ); y1.m2Subclass(); //syntax error. X objects have no m2Subclass() member. //**type of VARIABLE determines which methods CAN be called. //x1.m2Subclass(); //superclass reference variable referencing a subclass object: X x2 = new Y(); //super = sub //vice versa is not allowed. syntax error. //subclass reference variable can NOT reference a superclass object: //Y y2 = new X(); //sub != super //invoking a method via the superclass reference invokes the subclass //inherited: System.out.print( "x2.mInherited(): " ); x2.m1Inherited(); //overridden: System.out.print( "x2.m3Overridden(): " ); //overridden m3 of Y because x2 references a Y object! //**type of OBJECT determines which methods IS called. x2.m3Overridden(); //which method is actually called is polymorphically chosen based on //the type of object the superclass variable is referencing. //The same m3Overridden message sent to different types of objects //(Xs and Ys) has "many forms" of results: polymorphism // //subclass only: syntax error //although x2 references a Y object, it is a X variable. //x2 can only access the inherited/overridden X "part" of a Y object. //x2.m2Subclass(); //can downcast the X superclass reference variable to Y subclass //because it (x2) is referencing a Y object: System.out.print( "((Y)x2).m2Subclass(): " ); ((Y)x2).m2Subclass(); //if x2 were not referencing a Y object (eg. x2 = new X();), runtime error //e.g. x1 is referencing a X object: //((Y)x1).m2Subclass(); //ClassCastException error //Safe way to check if downcast if OK: if ( x2 instanceof Y ) { //true System.out.print( "((Y)x2).m2Subclass(): " ); ((Y)x2).m2Subclass(); //safely downcast from super to sub } if ( x1 instanceof Y ) { //false, so bad cast won't happen System.out.print( "((Y)x1).m2Subclass(): " ); ((Y)x1).m2Subclass(); } x1 = y1; //super = sub subclass object is-a superclass object if ( x1 instanceof Y ) { //now true System.out.print( "((Y)x1).m2Subclass(): " ); ((Y)x1).m2Subclass(); } //syntax error (even though x2 is now referencing a Y object) //y1 = x2; //sub != super superclass object is NOT a subclass object //compiler assumes you know what you're doing, let's downcasted thru: y1 = (Y)x2; //if x2 is not referencing a Y object at runtime, exception } } class X { public void m1Inherited() { System.out.println( "hello from X m1()" ); } public void m3Overridden() { System.out.println( "hello from X m3()" ); } } class Y extends X { //m1Inherited() is inherited from X. //m2Subclass() is in subclass Y only: public void m2Subclass() { System.out.println( "hello from Y m2()" ); } //m3Overridden is overridden public void m3Overridden() { System.out.println( "hello from Y m3()" ); } }