Intermediate Java 95 713 Sakir YUCEL MISMMSIT Carnegie

  • Slides: 36
Download presentation
Intermediate Java 95 -713 Sakir YUCEL MISM/MSIT Carnegie Mellon University Lecture: Polymorphism Slides adapted

Intermediate Java 95 -713 Sakir YUCEL MISM/MSIT Carnegie Mellon University Lecture: Polymorphism Slides adapted from Prof. Steven Roehrig

Today’s Topics o o o Upcasting again Method-call binding Why polymorphism is good Constructors

Today’s Topics o o o Upcasting again Method-call binding Why polymorphism is good Constructors and polymorphism Downcasting Several digressions: the Object class, object wrappers, the Class class, reflection

Upcasting Again class Cell. Phone { cell. Phone() { //…} public void ring(Tune t)

Upcasting Again class Cell. Phone { cell. Phone() { //…} public void ring(Tune t) { t. play(); } } class Tune { public void play() { System. out. println("Tune. play()"); } } class Obnoxious. Tune extends Tune{ Obnoxious. Tune() { // …} // … }

An Obnoxious. Tune “is-a” Tune public class Disrupt. Lecture { public static void main(String[]

An Obnoxious. Tune “is-a” Tune public class Disrupt. Lecture { public static void main(String[] args) { Cell. Phone noise. Maker = new Cell. Phone(); Obnoxious. Tune ot = new Obnoxious. Tune(); noise. Maker. ring(ot); // ot works though Tune called for } } Tune Obnoxious. Tune A “UML” diagram

Aspects of Upcasting o o o Upcasting is a cast The exact type of

Aspects of Upcasting o o o Upcasting is a cast The exact type of the object is lost What gets printed in the Cell. Phone example? Tune. play() (what were you expecting? ) Is this “the right thing to do”? The alternative: write separate ring() methods for each subclass of Tune?

Another Example class Cell. Phone { Cell. Phone() { //…} public void ring(Tune t)

Another Example class Cell. Phone { Cell. Phone() { //…} public void ring(Tune t) { t. play(); } } class Tune { Tune() { //…} public void play() { System. out. println("Tune. play()"); } } class Obnoxious. Tune extends Tune{ Obnoxious. Tune() { // …} public void play() { System. out. println("Obnoxious. Tune. play()"); } }

Polymorphism o o The second example prints Obnoxious. Tune. play() Since an Obnoxious. Tune

Polymorphism o o The second example prints Obnoxious. Tune. play() Since an Obnoxious. Tune object was sent to ring(), the Obnoxious. Tune’s play() method is called. This is called “polymorphism” Most people think it’s “the right thing to do”

Method-Call Binding o o The correct method is attached (“bound”) to the call at

Method-Call Binding o o The correct method is attached (“bound”) to the call at runtime. The runtime system discovers the type of object sent to ring(), and then selects the appropriate play() method: n n if it’s a Tune object, Tune’s play() is called if it’s an Obnoxious. Tune object, Obnoxious. Tune’s play() is called

Is Java Always Late-Binding? o o Yes, always, except for final, static and private

Is Java Always Late-Binding? o o Yes, always, except for final, static and private methods. final methods can’t be overridden, so the compiler knows to bind it. The compiler may be able to do some speed optimizations for a final method, but we programmers are usually lousy at estimating where speed bottlenecks are… In C++, binding is always at compile-time, unless the programmer says otherwise.

Another Polymorphism Example public static void main(String[] args) { Cell. Phone noise. Maker =

Another Polymorphism Example public static void main(String[] args) { Cell. Phone noise. Maker = new Cell. Phone(); Tune t; double d = Math. random(); if (d > 0. 5) t = new Tune(); else t = new Obnoxious. Tune(); noise. Maker. ring(t); }

References and Subclasses o o o We’ve seen that an Obnoxious. Tune object can

References and Subclasses o o o We’ve seen that an Obnoxious. Tune object can be supplied where a Tune object is expected. Similarly, a Tune reference can refer to an Obnoxious. Tune object. In both cases, the basic question is: “Is an Obnoxious. Tune really a proper Tune? ” Yes! This doesn’t work the other way around: any old Tune may not be an Obnoxious. Tune. So, an Obnoxious. Tune reference cannot refer to a Tune object.

Let’s Fool the Compiler! public static void main(String[] args) { Cell. Phone noise. Maker

Let’s Fool the Compiler! public static void main(String[] args) { Cell. Phone noise. Maker = new Cell. Phone(); Tune t 1 = new Tune(); Tune t 2 = new Obnoxious. Tune(); noise. Maker. ring(t 1); noise. Maker. ring((Tune)t 2); } Nothing changes… Obnoxious. Tune. play() is still printed.

Let’s Fool the Compiler! public static void main(String[] args) { Cell. Phone noise. Maker

Let’s Fool the Compiler! public static void main(String[] args) { Cell. Phone noise. Maker = new Cell. Phone(); Tune t 1 = new Tune(); Tune t 2 = new Obnoxious. Tune(); noise. Maker. ring(t 1); noise. Maker. ring((Obnoxious. Tune) t 2); } Nothing changes…

Let’s Fool the Compiler! public static void main(String[] args) { Cell. Phone noise. Maker

Let’s Fool the Compiler! public static void main(String[] args) { Cell. Phone noise. Maker = new Cell. Phone(); Tune t 1 = new Tune(); Tune t 2 = new Obnoxious. Tune(); noise. Maker. ring((Obnoxious. Tune) t 1); noise. Maker. ring(t 2); } This compiles, but gets a Cast. Class. Exception at runtime (even though Tune has a play() method). “Downcasting” can be dangerous!

Extensibility I public static void main(String[] args) { Cell. Phone noise. Maker = new

Extensibility I public static void main(String[] args) { Cell. Phone noise. Maker = new Cell. Phone(); Simple. Input keyboard = new Simple. Input(); System. out. println("Enter number of tunes: "); int num. Tunes = keyboard. next. Int(); Tune[] tunes = new Tune[num. Tunes]; for (int i = 0; i < num. Tunes; i++) { System. out. println("Enter tune type"); System. out. println("(Tune=1, Obnoxious. Tune=2)"); int tune. Type = keyboard. next. Int(); switch(tune. Type) { case 1: tunes[i] = new Tune(); break; case 2: tunes[i] = new Obnoxious. Tune(); break; default: tunes[i] = new Tune(); break; } } for (int i = 0; i < tunes. length; i++) noise. Maker. ring(tunes[i]);

Extensibility II public class Nice. Tune extends Tune { Nice. Tune() {} public void

Extensibility II public class Nice. Tune extends Tune { Nice. Tune() {} public void play() { System. out. println("Nice. Tune. play()"); } }

Extensibilty III public static void main(String[] args) { Cell. Phone noise. Maker = new

Extensibilty III public static void main(String[] args) { Cell. Phone noise. Maker = new Cell. Phone(); Simple. Input keyboard = new Simple. Input(); System. out. println("Enter number of tunes: "); int num. Tunes = keyboard. next. Int(); Tune[] tunes = new Tune[num. Tunes]; for (int i = 0; i < num. Tunes; i++) { System. out. println("Enter tune type"); System. out. println("(Tune=1, Obnoxious. Tune=2, Nice. Tune = 3)"); int tune. Type = keyboard. next. Int(); switch(tune. Type) { case 1: tunes[i] = new Tune(); break; case 2: tunes[i] = new Obnoxious. Tune(); break; case 3: tunes[i] = new Nice. Tune(); break; default: tunes[i] = new Tune(); break; } } for (int i = 0; i < tunes. length; i++) noise. Maker. ring(tunes[i]); }

Extensibility o o The Tunes program allows additional subclasses to be constructed and easily

Extensibility o o The Tunes program allows additional subclasses to be constructed and easily integrated. Polymorphism is the key in letting this happen. It was OK to make Tune objects. How to prevent creating base class objects?

Abstract Classes o o o As always, get the compiler involved in enforcing our

Abstract Classes o o o As always, get the compiler involved in enforcing our decisions. abstract class Glyph { abstract void draw(); Glyph() { System. out. println(“Glyph() before draw”); draw(); System. out. println(“Glyph() after draw”); } Now it’s a compiler error if we try to } make a Glyph object (but Glyph references are OK). Subclasses are abstract (and we must so state) until all abstract methods have been defined.

Glyph Example (cont. ) class Round. Glyph extends Glyph { int radius = 1;

Glyph Example (cont. ) class Round. Glyph extends Glyph { int radius = 1; Round. Glyph(int r) { radius = r; System. out. println(“Round. Glyph(), radius=“ + radius); } void draw() { System. out. println(“Round. Glyph. draw(), radius=“ + radius); } public class Glyph. Test { public static void main(String[] args) { new Round. Glyph(5); } }

Glyph Example (cont. ) o This produces o Guideline for constructors: Glyph() before draw()

Glyph Example (cont. ) o This produces o Guideline for constructors: Glyph() before draw() Round. Glyph. draw(), radius=0 Glyph() after draw() Round. Glyph(), radius= 5 n “Do as little as possible to set the object into a good state, and if you can possibly avoid it, don’t call any methods. ”

But What If Draw() Isn’t Abstract? abstract class Glyph { void draw() { System.

But What If Draw() Isn’t Abstract? abstract class Glyph { void draw() { System. out. println(“Glyph. draw()”); } abstract void do. Nothing(); // added to keep Glyph abstract Glyph() { System. out. println(“Glyph() before draw”); draw(); System. out. println(“Glyph() after draw”); } }

Glyph Example (cont. ) o o o The Glyph constructor is at work creating

Glyph Example (cont. ) o o o The Glyph constructor is at work creating the Glyph “sub-object” within a Round. Glyph object. draw() is overridden in Round. Glyph. A Round. Glyph object is being created. Round. Glyph’s draw() method is called Polymorphism rules!

Inheritance is Cool, But… Composition + Inheritance is Cooler abstract class Actor { public

Inheritance is Cool, But… Composition + Inheritance is Cooler abstract class Actor { public class Transmogrify { abstract void act(); public static void } main(String[] args){ class Happy. Actor extends Actor { Stage s = new Stage(); public void act() { //…} s. go(); //happy actor } s. change(); class Sad. Actor extends Actor { s. go() // sad actor public void act() { //…} } class Stage { Actor a = new Happy. Actor(); void change() { a = new Sad. Actor(); } void go() { a. act(); } }

H&C’s Tips for Inheritance o o o Place common operations and fields in the

H&C’s Tips for Inheritance o o o Place common operations and fields in the superclass. Don’t use protected fields very often. Use inheritance to model the “is-a” relationship. Don’t use inheritance unless all inherited methods make sense. Use polymorphism, not type information. Don’t overuse reflection.

Digression: The Object Class o o o Object is the ancestor of every class.

Digression: The Object Class o o o Object is the ancestor of every class. We don’t need to say, e. g. , class Employee extends Object We can use an Object reference to refer to any object. Object obj = new Employee(“Harry Hacker”, 35000); o But to use any of the methods of Employee, we must cast to the correct type. int salary = ((Employee) obj). get. Salary();

Some Object Methods o o clone(): Creates and returns a copy of this object.

Some Object Methods o o clone(): Creates and returns a copy of this object. Returns an Object. equals(): Indicates whether some other object is "equal to" this one. Returns a Boolean. get. Class(): Returns the runtime class of an object. Returns a Class. to. String(): Returns a string representation of the object. Returns a String.

The equals() Method o o As implemented in the Object class, this just tests

The equals() Method o o As implemented in the Object class, this just tests whether two references point to the same memory. This isn’t usually what we want. Override to get correct behavior. Pay attention to the Java language spec. to do it right.

The Rules For equals() o o o Reflexive: x. equals(x) is true Symmetric: if

The Rules For equals() o o o Reflexive: x. equals(x) is true Symmetric: if x. equals(y) then y. equals(x) Transitive: if x. equals(y) and y. equals(z) then x. equals(z) Consistent: if x. equals(y) now, and x and y don’t change, then x. equals(y) later If x is non-null, x. equals(null) is false

equals() Example class Employee { private String name; private double salary; private Date hire.

equals() Example class Employee { private String name; private double salary; private Date hire. Day; public boolean equals(Object other. Object) { if (this == other. Object) return true; if (other. Object == null) return false; if (get. Class() != other. Object. get. Class()) return false; Employee other = (Employee)other. Object; return name. equals(other. name) && salary == other. salary && hire. Day. equals(other. hire. Day); } }

Digression: Interpreting Strings as Numbers o To get an integer from the String s,

Digression: Interpreting Strings as Numbers o To get an integer from the String s, use int x = Integer. parse. Int(s); o o parse. Int() is a static method in Integer. Another way: Number. Format formatter = Number. Format. get. Number. Instance(); Number n = formatter. parse(s); o Number is an abstract class, and n is either a Double or a Long if (n instance. Of Double) Double d = (Double)n;

Digression: The Class o o We saw that polymorphism is accomplished by the Java

Digression: The Class o o We saw that polymorphism is accomplished by the Java runtime system, which keeps track of every object’s type. We can get the same information in code: Employee e; … Class cl = e. get. Class(); System. out. println(cl. get. Name() + " " + e. get. Name()); o Recall e can refer to an Employee or a Manager.

Other Ways to Get a Class o With the for. Name() method: String class.

Other Ways to Get a Class o With the for. Name() method: String class. Name = "Manager"; // the following line may throw a checked exception Class cl = Class. for. Name(class. Name); o or, more directly: Class c 11 = Manager. class; Class c 12 = int. class; Class c 13 = Double. class;

Digression in a Digression o A “checked exception” needs to be handled. try {

Digression in a Digression o A “checked exception” needs to be handled. try { String class. Name = …; // get class name // the following line may throw a checked exception Class cl = Class. for. Name(class. Name); … // do something with cl } catch(Exception e) { e. print. Stack. Trace(); }

Methods of the Class o The concept of “reflection” is supported by Class methods

Methods of the Class o The concept of “reflection” is supported by Class methods that let you discover superclass info, constructors, methods and fields. import java. lang. reflect. *; // defines Constructor, Field, Method … String class. Name = "Manager"; Class cl = Class. for. Name(class. Name); Field[] fields = cl. get. Declared. Fields(); for (int i = 0; i < fields. length; i++) System. out. println(fields[i]. get. Name());

“Growing” an Array static Object array. Grow(Object a) { Class cl = a. get.

“Growing” an Array static Object array. Grow(Object a) { Class cl = a. get. Class(); if (!cl. is. Array()) return null; Class component. Type = cl. get. Component. Type(); int length = Array. get. Length(a); int new. Length = length * 11 / 10 + 10; Object new. Array = Array. new. Instance (component. Type, new. Length); System. arraycopy(a, 0, new. Array, 0, length); return new. Array; }