Monday, October 14, 2013

Inheritance and Polymorphism


Quick Snapshot:

  • It is concept of making class use the properties and methods of another class while adding its own functionality.
  • It provides software re-usability.
  • Provides extensibility without disturbing existing features.
  • In java the keyword extends is used to establish the relationship between superclass and subclass to have inheritance. 
  • When you design with inheritance, you put common code in a class and then tell other more specific classes that the common (more abstract) class is their superclass. 
  • When one class inherits from another, the subclass inherits from the superclass.
  •  In Java, we say that the subclass extends the superclass. 
  • An inheritance relationship means that the subclass inherits the members of the superclass. 
  • When we say “members of a class” we mean the instance variables and methods.
  • super  keyword is used to access immediate super class shadowed variables. super.super is not valid.

Overriding and Polymorphism  

public class TestAnimals {
public static void main (String [] args) {
          Animal a = new Animal();
          Animal b = new Horse(); //Animal ref, but a Horse object
          a.eat(); // Runs the Animal version of eat()
         b.eat(); // Runs the Horse version of eat()
      }
}
class Animal {
     public void eat() {
         System.out.println("Generic Animal Eating Generically");
    }
}
class Horse extends Animal {
    public void eat() {
            System.out.println("Horse eating hay, oats, "+ "and horse treats");
    }
     public void buck() { }
}

In the preceding code, the test class uses an Animal reference to invoke a method
on a Horse object. Remember, the compiler will allow only methods in class Animal
to be invoked when using a reference to an Animal. The following would not be
legal given the preceding code:
Animal c = new Horse();
c.buck(); // Can't invoke buck(); // Animal class doesn't have that method 

 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 you didn't intend.
  • The return type must be the same as, or a subtype of, the return type declared in the original overridden method in the superclass. (More on this in a few pages when we discuss covariant returns.)
  • The access level can't be more restrictive than the overridden method's.
  • The access level CAN be less restrictive than that of the overridden method.
  • 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.
 Example:        
class A {
   public void foo() throws IOException {..}
}

class B extends A {
   @Override
   public void foo() throws SocketException {..} // allowed

   @Override
   public void foo() throws SQLException {..} // NOT allowed
}
SocketException extends IOException, but SQLException does not.
  •  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. We'll look at an example in a few pages when we discuss static methods in more detail.
  • If a method can't be inherited, you cannot override it. Remember that overriding implies that you're reimplementing a method you inherited! For example, the following code is not legal, and even if you added an eat() method to Horse, it wouldn't be an override of Animal's eat() method. 

Important Note

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 Animal {
public void eat() throws Exception {
// throws an Exception
}
}
class Dog2 extends Animal {
public void eat() { /* no Exceptions */}
public static void main(String [] args) {
Animal a = new Dog2();
Dog2 d = new Dog2();
d.eat(); // ok
a.eat(); // compiler error -
// unreported exception
}
}
This code will not compile because of the Exception declared on the Animal eat() method. This happens even though, at runtime, the eat() method used would be the Dog version, which does not declare the exception.  

Reason:

At compile time, the compiler doesn't know if the 'a' reference is referring to a Dog2 object or an Animal object. This will not be determined until runtime. Since the reference type of 'a' is Animal, the compiler assumes that the a.eat() method invocation is on an Animal object. The main() method must either throw an Exception exception object in its method declaration or it must 'catch' an Exception exception object when invoking the a.eat() method. That is it must 'Duck or Catch' the exception thrown by the Animal class a.eat() method. The compiler doesn't know that the Dog2 eat() method is invoked polymorphically, because this happens at runtime. The compiler only knows that the a.eat() method is invoked on an Animal reference at compile time. The compiler enforces the 'Duck or Catch' requirement for exception handling.  

Solution:

 class Animal {
        public void eat() throws Exception {
            // throws an Exception
        }
}
class Dog2 extends Animal {
          public void eat() { // no Exceptions declared  }
         public static void main (String [] args) throws Exception {
                Animal a = new Dog2();
                Dog2 d = new Dog2();
               d.eat();        // ok
               a.eat();        // no compiler error -
}


No comments:

Post a Comment