Friday, January 7, 2011

Chapter 8: Object Oriented Concepts - Polymorphism:

Polymorphism can be considered as the ability of one thing being multiple other things (though partially or fully). Am I confusing you? I believe yes. To put it in simpler words, any java object that can pass more than one Is-A test can be considered polymorphic. (Remember the Is-A and Has-A relationships that we looked at in the previous chapter?)
Other than objects of type Object, all Java objects are polymorphic in that they pass the IS-A test for their own type and for class Object.

Remember that the only way to access an object is through a reference variable, and there are a few key things to remember about references:
• A reference variable can be of only one type, and once declared, that type can never be changed (although the object it references can change).
• A reference is a variable, so it can be reassigned to other objects, (unless the reference is declared final).
• A reference variable’s type determines the methods that can be invoked on the object the variable is referencing.
• A reference variable can refer to any object of the same type as the declared reference, or—this is the big one—it can refer to any subtype of the declared type!
• A reference variable can be declared as a class type or an interface type. If the variable is declared as an interface type, it can reference any object of any class that implements the interface.

In the previous chapter on Inheritance we crdriveed a class Car that was extended by two other classes Ferrari and Porsche. Now lets say you want to add a fdriveure “open hood” to the cars. Not all cars have a retractable hood but at the same time you want this fdriveure for certain sports models of Ferrari and Porsche. How would you implement this?

Can we create a class with the openHood() method and only some of the subclasses of Ferrari or Porsche inherit from them? If we can do that then we can implement the feature wherein only selected models of Ferrari or Porsche sports cars can do that. But, unfortunately that wont work in Java. Remember the part on Multiple Inheritance towards the end of the last chapter? You cannot have a class that extends from two classes at the same time. So a code like this would never work:

Public class Porsche911TurboConvertible extends Porsche, Convertible {

}

A class cannot extend more than one class. That means one parent per class. A class can have multiple ancestors, however, since class B could extend class A, and class C could extend class B, and so on. So any given class might have multiple classes up its inheritance tree, but that’s not the same as saying a class directly extends two classes.

So if that doesn't work, what else options do we have? We could simple put the openHood() code inside Car class and disable the method in cars that cant open their hood. But, that is really bad design choice owing to multiple reasons. It makes the code error-prone, makes the class Cars less cohesive and it means that as per the code inside the Car class, all cars can open their hoods but the fact is only certain models can do that.

So what else could you do? You already know the answer—crdrivee a Convertible interface, and have only the Car subclasses that can open their hoods implement that interface. Here’s the interface:

public interface Convertible {
public void openHood();
}

And here’s the modified Porsche class that implements the interface:

class Porsche extends Car implements Convertible {
public void takePorscheForADrive() {
System.out.println("driving a Porsche");
}
public void openHood() {
System.out.println("Opening Hood...");
}
// more code
}

So now we have a Porsche that passes the IS-A test for both the Car class and the Convertible interface. That means a Porsche can be trdriveed polymorphically as one of four things at any given time, depending on the declared type of the reference variable:
• An Object (since any object inherits from Object)
• A Car (since Porsche extends Car)
• A Porsche (since that’s what it really is)
• An Convertible (since Porsche implements Convertible)

The following are all legal declarations. Look closely:
Porsche porscheCar = new Porsche();
Object o = porscheCar;
Car shape = porscheCar;
Convertible mover = porscheCar;

There’s only one object here—an instance of type Porsche—but there are four different types of reference variables, all referring to that one object on the heap.
This is polymorphism – the scenario where one object can be perceived or considered as multiple things.

There are two other important topics that we shall cover in this chapter:
• Method Overloading and
• Method Overriding

Overridden Methods

Any time you have a class that inherits a method from a superclass, you have the opportunity to override the method (unless, as you learned in the earlier chapters, the method is marked final). The key benefit of overriding is the ability to define behavior that’s specific to a particular subclass type. The following example demonstrates a Porsche subclass of Car overriding the Car version of the drive() method:

public class Car {
public void drive() {
System.out.println("Generic Car Driving Generically");
}
}

class Porsche extends Car {
public void drive() {
System.out.println("Porsche driving Full Throttle");
}
}

For abstract methods you inherit from a superclass, you have no choice. You must implement the method in the subclass unless the subclass is also abstract. Abstract methods must be implemented by the concrete subclass, but this is a lot like saying that the concrete subclass overrides the abstract methods of the superclass. So you could think of abstract methods as methods you’re forced to override.

The Car class creator might have decided that for the purposes of polymorphism, all Car subtypes should have an drive() method defined in a unique, specific way. Polymorphically, when someone has an Car reference that refers not to an Car instance, but to an Car subclass instance, the caller should be able to invoke drive() on the Car reference, but the actual runtime object (say, a Porsche instance) will run its own specific drive() method. Marking the drive() method abstract is the Car programmer’s way of saying to all subclass developers, “It doesn’t make any sense for your new subtype to use a generic drive() method, so you have to come up with your own drive() method implementation!” A (non-abstract), example of using polymorphism looks like this:

public class TestCars {
public static void main (String [] args) {
Car a = new Car();
Car b = new Porsche(); //Car ref, but a Porsche object
a.drive(); // Runs the Car version of drive()
b.drive(); // Runs the Porsche version of drive()
}
}
class Car {
public void drive() {
System.out.println("Generic Car Driveing Generically");
}
}
class Porsche extends Car {
public void drive() {
System.out.println("Porsche driving Full Throttle");
}
public void brake() { }
}

In the preceding code, the test class uses a Car reference to invoke a method on a Porsche object. Remember, the compiler will allow only methods in class Car to be invoked when using a reference to a Car. The following would not be legal given the preceding code:

Car c = new Porsche();
c.brake(); // Can't invoke brake();
// Car class doesn't have that method

To reiterate, the compiler looks only at the reference type, not the instance type. Polymorphism lets you use a more abstract supertype (including an interface) reference to refer to one of its subtypes (including interface implementers).

The overriding method cannot have a more restrictive access modifier than the method being overridden (for example, you can’t override a method marked public and make it private). Think about it: if the Car class advertises a public drive() method and someone has an Car reference (in other words, a reference declared as type Car), that someone will assume it’s safe to call drive() on the Car reference regardless of the actual instance that the Car reference is referring to. If a subclass were allowed to sneak in and change the access modifier on the overriding method, then suddenly at runtime—when the JVM invokes the true object’s (Porsche) version of the method rather than the reference type’s (Car) version—the program would die a horrible death. Let’s modify the polymorphic example we saw earlier in this section:

public class TestCars {
public static void main (String [] args) {
Car a = new Car();
Car b = new Porsche(); //Car ref, but a Porsche object
a.drive(); // Runs the Car version of drive()
b.drive(); // Runs the Porsche version of drive()
}
}
class Car {
public void drive() {
System.out.println("Generic Car Driveing Generically");
}
}
class Porsche extends Car {
private void drive() { // whoa! - it's private!
System.out.println("Porsche driving Full Throttle");
}
}
If this code compiled (which it doesn’t), the following would fail at runtime:
Car b = new Porsche(); // Car ref, but a Porsche
// object , so far so good
b.drive(); // Chaos at runtime!


The variable b is of type Car, which has a public drive() method. But remember that at runtime, Java uses virtual method invocation to dynamically select the actual version of the method that will run, based on the actual instance. A Car reference can always refer to a Porsche instance, because Porsche IS-A Car. What makes that superclass reference to a subclass instance possible is that the subclass is guaranteed to be able to do everything the superclass can do. Whether the Porsche instance overrides the inherited methods of Car or simply inherits them, anyone with a Car reference to a Porsche instance is free to call all accessible Car methods. For that reason, an overriding method must fulfill the contract of the superclass.

The rules for overriding a method are as follows:
• The argument list must exactly match that of the overridden method. If they don’t match, you can end up with an overloaded method that you didn’t intend on creating.
• The return type must be the same as, or a subtype of, the return type declared in the original overridden method in the superclass.
• The access level can’t be more restrictive than the overridden method’s. (public to private not allowed)
• The access level CAN be less restrictive than that of the overridden method. (private to public allowed)
• Instance methods can be overridden only if they are inherited by the subclass. A subclass within the same package as the instance’s superclass can override any superclass method that is not marked private or final. A subclass in a different package can override only those non-final methods marked public or protected (since protected methods are inherited by the subclass).
• The overriding method CAN throw any unchecked (runtime) exception, regardless of whether the overridden method declares the exception.
• The overriding method must NOT throw checked exceptions that are new or broader than those declared by the overridden method. For example, a method that declares a FileNotFoundException cannot be overridden by a method that declares a SQLException, Exception, or any other non-runtime exception unless it’s a subclass of FileNotFoundException.
• The overriding method can throw narrower or fewer exceptions. Just because an overridden method “takes risks” doesn’t mean that the overriding subclass’ exception takes the same risks. Bottom line: an overriding method doesn’t have to declare any exceptions that it will never throw, regardless of what the overridden method declares.
• You cannot override a method marked final.
• You cannot override a method marked static.
• If a method can’t be inherited, you cannot override it. Remember that overriding implies that you’re re-implementing a method you inherited! For example, the following code is not legal, and even if you added an drive() method to Porsche, it wouldn’t be an override of Car’s drive() method.

public class TestCars {
public static void main (String [] args) {
Porsche h = new Porsche();
h.drive(); // Not legal because Porsche didn't inherit drive()
}
}
class Car {
private void drive() {
System.out.println("Generic Car Driveing Generically");
}
}
class Porsche extends Car { }

Invoking a Superclass Version of an Overridden Method

Often, you’ll want to take advantage of some of the code in the superclass version of a method, yet still override it to provide some additional specific behavior. It’s like saying, “Run the superclass version of the method, then come back down here and finish with my subclass additional method code.” It’s easy to do in code using the keyword super as follows:

public class Car {
public void drive() { }
public void changeGear() {
// Useful change gear code goes here
}
}
class Porsche extends Car {
public void changeGear() {
// Take advantage of Car code, then add some more
super.changeGear(); // Invoke the superclass (Car) code
// Then do Porsche-specific change gear work here
}
}

Note: Using super to invoke an overridden method only applies to instance methods. (Remember, static methods can’t be overridden.)

If a method is overridden but you use a polymorphic (supertype) reference to refer to the subtype object with the overriding method, the compiler assumes you’re calling the supertype version of the method. If the supertype version declares a checked exception, but the overriding subtype method does not, the compiler still thinks you are calling a method that declares an exception. Let’s take a look at an example:
class Car {
public void drive() throws Exception {
// throws an Exception
}
}
class Car2 extends Car {
public void drive() { /* no Exceptions */}
public static void main(String [] args) {
Car a = new Car2();
Car2 d = new Car2();
d.drive(); // ok
a.drive(); // compiler error - unreported exception
}
}

This code will not compile because of the Exception declared on the Car drive() method. This happens even though, at runtime, the drive() method used would be the Car2 version, which does not declare the exception.

Examples of Illegal Method Overrides

Let’s take a look at overriding the drive() method of Car:
public class Car {
public void drive() { }
}

Let us take a look at a few examples of overridden drive() methods taking the above version of the method in the Car Class

1. private void drive() {} – Access Modifier is more restrictive
2. public void drive() throws IOException {} – Declares a checked exception that is not defined by the super class
3. public void drive (String road) {} – A legal overload but not an override because the arguments to the method has changed (Don't bother about overload just yet, that is the next paragraph)
4. public String drive() {} – Not an override because of the return type and at the same time, not an overload either because there is no change in the argument list.

Overloaded Methods

Experienced java programmers can clearly identify the difference between overloaded methods and the overridden ones. We just had a detailed look at overridden methods and it is time to take a look at the overloaded ones.

Overloaded methods let you reuse the same method name in a class, but with different arguments (and optionally, a different return type). Overloading a method often means you’re being a little nicer to those who call your methods, because your code takes on the burden of coping with different argument types rather than forcing the caller to do conversions prior to invoking your method. The rules are simple:

• Overloaded methods MUST change the argument list.
• Overloaded methods CAN change the return type.
• Overloaded methods CAN change the access modifier.
• Overloaded methods CAN declare new or broader checked exceptions.
• A method can be overloaded in the same class or in a subclass. In other words, if class A defines a doStuff(int i) method, the subclass B could define a doStuff(String s) method without overriding the superclass version that takes an int. So two methods with the same name but in different classes can still be considered overloaded, if the subclass inherits one version of the method and then declares another overloaded version in its class definition.

Tip: Be careful to recognize when a method is overloaded rather than overridden. You might see a method that appears to be violating a rule for overriding, but that is actually a legal overload, as follows:
public class Car {
public void drive(int y, String s) { }
public void brake(int x) { }
}
class Ferrari extends Car {
public void drive(int y, long s) throws IOException { }
}
It’s tempting to see the IOException as the problem, because the overridden drive() method doesn’t declare an exception, and IOException is checked by the compiler. But the drive() method is not overridden! Subclass Ferrari overloads the drive() method, by varying the argument list, so the IOException is fine.

Legal Overloads

Let us say we have the Ferrari class with a method drive() as below:
Public class Ferrari extends Car {
Public void drive() {…}
}

Below are the legal overload method declarations:

1. public void drive(String destination, String roadToTake) {…}
2. public void drive(String destination, int timeToTake) {…}
3. public void drive(String destination) throws RouteNotFoundException {…}
Assuming that the Ferrari class has the above 3 methods, below are is an illegal overload:
1. public String drive (String destination, String roadToTake) {…} – You cannot have two methods which differ just in the return type. This is not allowed

Invoking Overloaded Methods

When you have multiple methods that have the same name, it is sometimes confusing for the programmer who is analysing the code to determine which method would get called and when. But, the good news is that the compiler does not have this dilemma.

When a method is invoked, more than one method of the same name might exist for the object type you’re invoking a method on. For example, the Ferrari class might have three methods with the same name but with different argument lists, which means the method is overloaded. (Check example methods above)

The compiler checks the list of arguments passed to the method before deciding which method to call. Let us say you call the drive method with only the String argument “Destination” the compiler knows exactly that the 3rd method that takes only one String as argument is the method to be invoked. The other 2 methods wouldn't be touched at all during program execution.
Example:

Public class Ferrari extends Car {
public void drive(String destination, String roadToTake) {…}
public void drive(String destination, int timeToTake) {…}
public void drive(String destination) throws RouteNotFoundException {…}
}
Let us say we have the above Ferrari class.

Public class FerrariTest {
Public static void main(String[] args) {
Ferrari car = new Ferrari();
Car.drive(“Central Railway Station”);
}
}

In the preceding code, the FerrariTest class invokes a drive method with just one String argument. So the compiler scans the Ferrari class to see if there is a method drive() that takes one string argument and since it finds one, it invokes it.

Invoking overloaded methods that take object references rather than primitives is a little more interesting. Say you have an overloaded method such that one version takes an Car and one takes a Porsche (subclass of Car). If you pass a Porsche object in the method invocation, you’ll invoke the overloaded version that takes a Porsche. Or so it looks at first glance:

class Car { }
class Porsche extends Car { }
class UseCars {
public void doStuff(Car a) {
System.out.println("In the Car version");
}
public void doStuff(Porsche h) {
System.out.println("In the Porsche version");
}
public static void main (String [] args) {
UseCars ua = new UseCars();
Car CarObj = new Car();
Porsche PorscheObj = new Porsche();
ua.doStuff(CarObj);
ua.doStuff(PorscheObj);
}
}

The output is what you expect:
in the Car version
in the Porsche version

But what if you use an Car reference to a Porsche object?
Car CarRefToPorsche = new Porsche();
ua.doStuff(CarRefToPorsche);

Which of the overloaded versions is invoked? You might want to say, “The one that takes a Porsche, since it’s a Porsche object at runtime that’s being passed to the method.” But that’s not how it works. The preceding code would actually print:
in the Car version

Even though the actual object at runtime is a Porsche and not an Car, the choice of which overloaded method to call (in other words, the signature of the method) is NOT dynamically decided at runtime. Just remember, the reference type (not the object type) determines which overloaded method is invoked! To summarize, which overridden version of the method to call (in other words, from which class in the inheritance tree) is decided at runtime based on object type, but which overloaded version of the method to call is based on the reference type of the argument passed at compile time. If you invoke a method passing it a Car reference to a Porsche object, the compiler knows only about the Car, so it chooses the overloaded version of the method that takes an Car. It does not matter that at runtime there’s actually a Porsche being passed.

Polymorphism in Overloaded and Overridden Methods

How does polymorphism work with overloaded methods? From what we just looked at, it doesn’t appear that polymorphism matters when a method is overloaded. If you pass an Car reference, the overloaded method that takes an Car will be invoked, even if the actual object passed is a Porsche. Once the Porsche masquerading as Car gets in to the method, however, the Porsche object is still a Porsche despite being passed into a method expecting an Car. So it’s true that polymorphism doesn’t determine which overloaded version is called; polymorphism does come into play when the decision is about which overridden version of a method is called. But sometimes, a method is both overloaded and overridden. Imagine the Car and Porsche classes look like this:

public class Car {
public void drive() {
System.out.println("Generic Car Driving Generically");
}
}
public class Porsche extends Car {
public void drive() {
System.out.println("Porsche driving Fast ");
}
public void drive(String s) {
System.out.println("Porsche driving " + s);
}
}

Notice that the Porsche class has both overloaded and overridden the drive() method.

Tip: Don’t be fooled by a method that’s overloaded but not overridden by a subclass. It’s perfectly legal to do the following:
public class Car {
void drive() { }
}
class Ferrari extends Car{
void drive(String s) { }
}

The Ferrari class has two drive() methods: the no-arg version it inherits from Car (and does not override), and the overloaded drive(String s) defined in the Ferrari class. Code with a reference to a Car can invoke only the no-arg version, but code with a reference to a Ferrari can invoke either of the overloaded versions.


Differences between Overloaded and Overridden Methods:

  Overloaded Methods Overridden Methods
Arguments Must Change Must Not Change
Return Type Can Change Cannot Change (Except for Covariant Returns)
Access Can Change Cannot Change to more restrictive (Can be less restrictive)
Invocation Reference type determines which overloaded version (based on declared argument types) is selected. Happens at compile time. The actual method that’s invoked is still a virtual method invocation that happens at runtime, but the compiler will already know the signature of the method to be invoked. So at runtime, the argument match will already have been nailed down, just not the class in which the method lives. Object type (in other words, the type of the actual instance on the heap) determines which method is selected. Happens at runtime.
Constructor Overloading is actually another topic that would be a part of this polymorphism/overloading discussion but we will be covering that as part of one of the subsequent chapters.

Previous Chapter: Chapter 7: Object Oriented Concepts - Inheritance

Next Chapter: Chapter 9: Reference Variable Casting

6 comments:

  1. Hi Anand,

    I have a confusion as stated below:

    public class Car {
    public void drive()
    {
    System.out.println("drive a Car");
    }
    }

    public class Ferarri extends Car {

    public void drive()
    {
    System.out.println("drive a ferrari");
    }
    public void brake()
    {
    System.out.println("apply brakes in ferrari");
    }
    }

    public class TestClass {

    public static void main(String[] args) {
    Car c=new Ferarri();
    c.drive();//this gives output:drive a ferrari
    c.brake();// this doesn't compile
    }
    }

    My question is if reference is of supertype which is refering to the instance of subtype then if we call a method using supertype reference then it invokes overriden method (shouldn't it call superclass method?) but if we call a method which is extra in subclass then it gives compilation error saying superclass doesn't have this method (now, as per previous execution shouldn't it call subclass method again?). Please clear.

    ReplyDelete
    Replies
    1. Rahul - When you call the c.brake() the system looks for a method named brake in the "Car" class because, even though the new command created an object Ferrari, the object got tagged to its super type "Car".

      You have already answered your question in your comment - (shouldn't it call superclass method?)

      Yes, the system tries to call the superclass method and hence the compile error. The system cannot automatically call the child class method because as per instance declaration, even though created a ferrari object, you are working with a car object and that object does not have a drive method.

      Hope this clears your doubt

      Anand

      Delete
  2. Hi Ananad,

    I am cleared with c.brake(); Since the class Car doesn't have any brake() method, the program won't compile.

    But, what about c.drive()?
    It is not calling drive method of class Car (though we have one over there). Instead, it is calling drive method of Ferrari (that's why the output is 'drive a ferrari' instead of 'drive a car'). Why so?

    ReplyDelete
    Replies
    1. Hi Rahul,
      At run time, the system knows that the object that got created is actually a Ferrari (Car c=new Ferarri();) and that is why it calls the Ferrari class' drive() method.

      But, at compile time, since the object on the left hand side is a "Car" the compiler tries to see if Car has a method named "brake" and since it cant find it, it throws up the compilation error.

      What we have just seen is a classic example of run time and compile time polymorphism.

      Does this explain?

      Anand

      Delete
    2. Yes Anand, it is crystal clear now!

      My understanding is as below:
      *Compile Time Scenario-As reference variable c is of type Car, at the compile time the compiler will check whether the class Car has implementations for the methods being called (in this case- c.drive() & c.brake()). If class Car has these methods then compiler will pass them otherwise it will give out errors(that's why c.drive() is passed while c.brake is failed.)

      *Run Time Scenario-Once compilation is successful,at the runtime before calling a method JVM will check the actual object created (new Ferrari() in this case) and it will invoke the method belonging to the Ferrari class (i.e. drive() method in class Ferarri)

      Correct me if I am wrong.

      THANKS A LOT ONCE AGAIN! :-)

      Delete
    3. You are 100% correct buddy and you are very welcome.

      Anand

      Delete

© 2013 by www.inheritingjava.blogspot.com. All rights reserved. No part of this blog or its contents may be reproduced or transmitted in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without prior written permission of the Author.

ShareThis

Google+ Followers

Followers