CS 596 Java Programming Fall Semester, 1998 Inheritance |
||
---|---|---|
© 1998, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 05-Oct-98 |
Inheritance
class Parent { public int parentVariable = 10; public void parentFunction() { System.out.println( "Parent Function" ); } } class Child extends Parent { public void childFunction() { parentFunction(); System.out.println( "In Child " + parentVariable ); } } class Inheritance { public static void main( String args[] ) { Child example = new Child(); example.childFunction(); example.parentFunction(); System.out.println( example.parentVariable ); } }Output Parent Function In Child 10 Parent Function 10
Inheritance
A class can be extended or subclassed
The class that is extended is a superclass
Some people use the words parent class or base class to mean a superclass
The extended class is a subclass or extended class of its superclass
Some people use the word child class to mean subclass
An object created from the subclass has its own copy of all the nonstatic fields defined in its superclass
Each class defines a type. We can declare variables & fields of the new type
A subclass defines a subtype of its parent class.
Java does not support multiple inheritance
Class Object
All classes inherit directly or indirectly from java.lang.Object
Is short hand for
class Parent { int size; } class Child extends Parent { int age; } class TestObject { public static void main( String args[] ) { Parent watchThis = new Parent(); int myHash = watchThis.hashCode(); System.out.println( myHash ); // Where does hashCode come from? } }
Object's Methods (minus thread related)
clone()
Casting and Classes An instance of a child class can be assigned to a variable (field) of the parent class. If a variable references an object of a subclass, the object can be cast down to its actual type with an explicit cast. The explicit cast is required. A runtime error occurs when explicitly casting an object to a type that it is not. In the code below the cast "(Uncle) object" is a runtime error because at that time object holds an instance of the Child class, which is not of type (or subclass) of Uncle. An object of type Parent cannot be cast to type Child.
class Parent { int data; } class Child extends Parent { String name; } class Uncle { String rich; } class Casting { public static void main( String args[] ) { Object object; Parent parent; Child child = new Child(); Uncle uncle; parent = child; object = child; parent = (Parent) object; // explicit cast down child = (Child) object; // explicit cast down uncle = (Uncle) object; //Runtime exception } }Output java.lang.ClassCastException: Child: cannot cast to Uncle
Inheritance and Name Clashes
What happens when the parent class has a method(field) with the same name as a method(field) of the child class?
Terms and Rules
Overloading
Hiding Fields A parent and child class can both have non-static fields with the same name. An instance of the child class will always contain both fields, even if access levels on the parent field do not allow the child to access the parent's field directly. Determine which "name" will be used statically.
class Parent { public String name = "Parent"; public void parentPrint() { System.out.println( name ); } } class Child extends Parent { String name = "Child"; public void print() { System.out.println( name ); } } class HiddenFields { public static void main( String args[] ) { Child whoAmI = new Child(); whoAmI.print(); whoAmI.parentPrint(); System.out.println( whoAmI.name ); System.out.println( ( (Parent ) whoAmI).name ); } }Output Child Parent Child Parent
Overriding Methods Note that the statement "overRidden.print();" prints out different text depending on what type of object overRidden references.
class Parent { public void print() { System.out.println( "In Parent" ); } } class Child extends Parent { public void print() { System.out.println( "In Child" ); } } class OverriddingMethods { public static void main( String args[] ) { Child whoAmI = new Child(); whoAmI.print(); Parent overRidden = whoAmI; overRidden.print(); overRidden = new Parent(); overRidden.print(); } }
Output In Child In Child In Parent
Why not the same rule for Methods and Fields?
class Parent { public int name = 5; public void test() { System.out.println( "In Parent \t" + (name / 3) ); } } class Child extends Parent { public String name = "Can't divide me"; }
class FieldsMustBeResolveStaticly { public static void main( String args[] ) { Child trouble = new Child(); trouble.test(); } }
Fancy Accesses Since method access is determined dynamically, methods that methods call are also determined dynamically. Since Child overrides the "print" method, which "print" is called in "whichGetsCalled" is also determined dynamically.
class Parent { private String name = "Parent"; public void print() { System.out.println( "\t In Parent" ); } public void whichGetsCalled() { System.out.println( "In whichGetsCalled " + name ); print(); } } class Child extends Parent { private String name = "Child"; public void print() { System.out.println( "\t In Child" ); } } class Polymorphism { public static void main( String args[] ) { Parent whoAmI = new Child(); whoAmI.whichGetsCalled(); whoAmI = new Parent(); whoAmI.whichGetsCalled(); } }Output In whichGetsCalled Parent In Child In whichGetsCalled Parent In Parent
This Again
this
class Parent { String name = "Parent"; public void print() {System.out.println( "In Parent" + name ); } public void thisFunction() { System.out.println( this.name ); this.print(); } } class Child extends Parent { String name = "Child"; public void print() { System.out.println( "In Child " + name ); } } class This { public static void main( String args[] ) { Parent parentThis = new Child(); parentThis.thisFunction(); Child childThis = new Child(); childThis.thisFunction(); } }
Overloading, Overriding and Return Types When overriding a method, the child's method must have the same signature and return type as the parent's method. In C++ if the parent class overloads a method name (i.e. print) and the child class overrides one of these methods, then the child can not directly access any of the overloaded parent's methods.
class Parent { public void print() { System.out.println( "In Parent" ); } public void print( String message ) { System.out.println( "In Parent" + '\t' + message ); } public void print( int value ) { System.out.println( "In Parent" + '\t' + value ); } } class Child extends Parent { public int print() { // Compiler Error System.out.println( "In Child" ); return 5; } public void print( String message ) { System.out.println( "In Child" + '\t' + message ); } } class ReturnTypesCount { public static void main( String args[] ) { Child whoAmI = new Child(); whoAmI.print(); whoAmI.print( "Hi Mom" ); whoAmI.print( 10 ); // Ok in Java, // Compile error in C++ } }
Super
super
class Parent { String name = "Parent"; } class Child extends Parent { String name = "Child"; public void print() { System.out.println( name ); System.out.println( super.name ); } } class GrandChild extend Child { String name = "GrandChild"; } class SuperMain { public static void main( String args[] ) { GrandChild whoAmI = new GrandChild(); whoAmI.print(); } }
Child Parent
super.super
Java does not allow you to chain supers to refer to your grandparent class
super.super is not allowed in Java
The need to reference a grandparent's class should be very rare
Constructors and Inheritance
Before the constructor in a Child class is called, its parent's constructor will be called
If the Child class constructor does not do this, the parent's constructor with no arguments will be implicitly called
super( arg list) as the first line in a constructor explicitly calls the parent's constructor with the given signature
If
Implicit Call to Parent's Constructor
class Parent { public Parent() { System.out.println( "\t In Parent" ); } } class Child extends Parent { public Child() { System.out.println( "\t In Child" ); } public Child( String notUsed ) { System.out.println( "\t In Child with argument" ); } } class Constructors { public static void main( String args[] ) { System.out.println( "Construct Parent" ); Parent sleepy = new Parent(); System.out.println( "Construct Child" ); Child care = new Child(); System.out.println( "Construct Second Child" ); care = new Child( "Try Me" ); } }Output Construct Parent In Parent Construct Child In Parent In Child Construct Second Child In Parent In Child with argument
Explicit Call to Parent's Constructors
class Parent { public Parent( ) { System.out.println( "In Parent, No Argument" ); } public Parent( String message ) { System.out.println( "In Parent" + message ); } } class Child extends Parent { public Child( String message, int value ) { this( message ); // if occurs must be first System.out.println( "In Child" ); } public Child( String message ) { super( message ); // if occurs must be first System.out.println( "In Child" + message ); } } class Constructors { public static void main( String args[] ) { System.out.println( "Construct Child" ); Child care = new Child( ">Start from Child<", 5 ); } }Output Construct Child In Parent>Start from Child< In Child>Start from Child< In Child
No Default Parent Constructor
The compiler will not generate the default constructor for the class "Parent", since "Parent" has a constructor. The class "Child" makes an implicit call to the default constructor in "Parent". This causes a compile error.
class Parent { public Parent( String message ) { System.out.println( "In Parent" + message ); } } class Child extends Parent { public Child( ) { // Compile Error System.out.println( "In Child" ); } public Child( String message ) { super( message ); System.out.println( "In Child" + message ); } }
Nonstatic Methods in Constructors are Resolved Dynamically
Methods called in constructors follow the rules.
class Parent { public Parent( ) { whichOne( ); } public void whichOne( ) { System.out.println( "In Parent" ); } } class Child extends Parent { public void whichOne( ) { System.out.println( "In Child" ); } } class Test { public static void main( String args[] ) { new Child( ); } }Output In Child
Static Methods
Static methods references are resolved statically
class Parent { public static void classFunction() { System.out.println( "In Parent" ); } public static void inheritMe() { System.out.println( "Inherited" ); } } class Child extends Parent { public static void classFunction() { System.out.println( "In Child" ); } } class StaticTest { public static void main( String args[] ) { Parent exam = new Child(); exam.classFunction(); Child question = new Child(); question.classFunction(); question.inheritMe(); } }Output In Parent In Child Inherited
Access Levels and Inheritance
Private class members are accessible only in the class they are defined
class Private { private String name = "Private"; public void print() { System.out.println( name ); } public void tricky( Private argument ) { argument.name = "This access is legal"; } } class Excluded extends Private { public void cantPrint() { System.out.println( name ); // Compile error } } class PrivateExample { public static void main( String[] args ) { Private top = new Private(); Private bottom = new Private(); top.tricky( bottom ); bottom.print(); } }Output (if compile error is removed ) This access is legal
Private and Dynamic Method Binding
Since a private method can not be called from the child, when an inherited method (foo) calls the parent private method (Parent.bar), the parent private method is always the one called
class Madre { public void foo() { bar(); } private void bar() { System.out.println( "Madre bar"); } } class Hija extends Madre { public void bar() { System.out.println( "Hija bar"); } public static void main( String[] args ) { Hija confundido = new Hija (); confundido.foo(); } }Output Madre bar
Protected Access and Inheritance protected
package Botany; public class Protected { protected String name = "Protected"; } package Botany; public class NoRelation { public void SampleAccess( Protected accessOK ) { accessOK.name = "This is legal"; } } package Tree; public class NoRelationEither { public void SampleAccess( Botany.Protected noAccess) { noAccess.name = "This is a compile Error"; } } package Tree; public class Child extends Botany.Protected { public void SampleAccess( Botany.Protected noAccess, Child accessOK) { name = "This is legal, I am related"; noAccess.name = "This is a compile Error"; accessOK.name = "This is legal"; } }
Protected Access and Inheritance: The Rules [1]
Let C be a class with a protected member or constructor.
Let S be a subclass of C in a different package.
The declaration of the use of the protected member or constructor occurs in S.
If the access is of a protected member, let Id be its name
Package Access and Inheritance Package Access
package Botany; public class NoExplicit { String name = "No explicit access level given"; } package Botany; public class NoRelation { public void SampleAccess( NoExplicit accessOK ) { accessOK.name = "This is legal"; } } package Tree; public class NoRelationEither { public void SampleAccess( Botany.NoExplicit noAccess) { noAccess.name = "This is a compile Error"; } } package Tree; public class Child extends Botany.NoExplicit { public void SampleAccess( Botany.NoExplicit noAccess, Child alsoNoAccess) { name = "I am related, but this is NOT LEGAL"; noAccess.name = "This is a compile Error"; alsoNoAccess.name = "This is a compile Error"; } }
Inheritance of Access Levels
The access modifier of an overriding method must provide at least as much access as the overridden method
Note
A private method is not accessible to subclasses, so can not be overridden in the technical sense.
Thus, a subclass can declare a method with the same signature as a private method in one of its superclasses. There is no requirement that the return type or throws clause of the method bear any relationship to those of the private method in the superclass
class Parent { protected void foo() {}; public void bar() {}; private void noSeeMe() {}; } class Child extends Parent { public void foo() {}; // OK protected void bar() {}; // Compile Error public int noSeeMe() { return 5;}; //OK }
Rules Are Different for Fields
Since fields are hidden, not overridden we can set the access permission on fields in the class "Child" independent of the access permissions of the fields in class "Parent"
class Parent { protected int foo; public int bar; } class Child extends Parent { public int foo; // OK protected int bar; // OK }
Inheritance and Final
The final modifier provides:
final class EndOfTheLine { int noSubclassPossible; public void aFunction() { System.out.println( "Hi Mom" ); } }
class ThisWillNotWork extends EndOfTheLine { int ohNo; }Does not compile
Final Method
A final method can not be overwritten
class Parent { public final void theEnd() { System.out.println( "This is it" ); } public void normal() { System.out.println( "In parent" ); } } class Child extends Parent { public void theEnd() { // Compile Error System.out.println( "Two Ends?" ); } public void normal() { System.out.println( "In Child" ); } }
Abstract Classes
abstract class NoObjects { int noDirectObjectPossible = 5; public void aFunction() { System.out.println( "Hi Mom" ); } public abstract void subClassMustImplement( int foo ); } class Concrete extends NoObjects { public void subClassMustImplement( int foo ) { System.out.println( "In Concrete" ); } } class AbstractClasses { public static void main( String args[] ) { NoObjects useful = new Concrete(); Concrete thisWorks = new Concrete(); useful.subClassMustImplement( 10 );
System.out.println( thisWorks.noDirectObjectPossible ); } }Output In Concrete 5
Relationships between Classes
Is-kind-of or is-ais-analogous-tois-part-of or has-a
Is-kind-of, is-a, is-a-type-of
Let A and B be classes
To determine if A should be the parent class of B ask:
Is-kind-of, is-a, is-a-type-of
Example 2 Employees
Consider Employee, Manager, Engineer, and FileClerk
A Manager is a kind of an Employee
So should we make Employee a parent class of a Manager class?
Is Manager a class or a role that an Employee object will play?
class Employee { //lots of stuff here not shown } class Driver { public static void main( String[] args ) { Employee manager = new Employee( "Sally" ); Employee engineer = new Employee( "Smith" ); } }
Example 2 Employees - Continued
If Manager, etc are classes then several options:
Option 1 Direct inheritance
class Employee { // stuff not shown } class Manager extends Employee { // stuff not shown }
Option 2 Separate Inheritance Structure
class Employee { Role position; // stuff not shown } class Role { // stuff not shown } class Manager extends Role { // stuff not shown }
is-analogous-to
If class X is-analogous-to class Y then look for superclass
Example BankAccounts
A checking account is analogous to a saving account
is-part-of or has-a
If class A is-part-of class B then there is no inheritance
Some negotiation between A and B for responsibilities may be needed
Example: Linked-List class and a Stack
A stack uses a linked-list in its implementation
A stack has a linked-list
The Stack class and the LinkedList class are separate classes
class LinkedList { public void addFront( Object item) { // not shown} public Object removeFront() { // not shown} public Object removeEnd() { // not shown} } class Stack { LinkedList stackElements = new LinkedList(); public void push( Object item ) { stackElements.addFront( item); } }
A Common Mistake
Using inheritance with a is-part-of (has-a) relation between classes
class LinkedList { // fields not shown public void addFront( Object item) { // not shown} public Object removeFront() { // not shown} public Object removeEnd() { // not shown} } class Stack extends LinkedList { public void push( Object item ) { addFront( item); } }
class Test { public static void main( String[] arguments) { Stack problem = new Stack(); problem.push( "Hi"); problem.push( "Mom" ); problem.removeEnd(); // ???? } }[1]From The Java Language Specification, Gosling, Joy, Steele, 1996, section 6.6.2, page 100
Copyright © 1998 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
All rights reserved.
visitors since 22-Sep-98