| CS 535: Object-Oriented Programming & Design |
|
|---|
Fall Semester, 1997
Doc 9, Inheritance
To Lecture Notes Index
San Diego State University -- This page last updated 29-Sep-97
Contents of Doc 9, Inheritance
- References
- Inheritance
- Super
- This Again
- Static Methods
- Access Levels and Inheritance
- Inheritance and Final
- Constructors and Inheritance
- Class Object
- Abstract Classes
The Java Programming Language, Arnold and Gosling, 1996, Chapter 3
The Java Language Specification, Gosling, Joy, Steele, 1996, section
6.6
java.lang.Object source code, rohan:/opt/java/src/java/lang/Object.java
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
Hiding Fields
class Parent {
public String name = "Parent";
public int test;
public void parentPrint() {
System.out.println( name );
}
}
class Child extends Parent {
public String name = "Child";
public String test;
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
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 ) whoAmI).print();
}
}
Output
In Child
In Child
Terms and Rules
Overloading
- Providing more than one method with the same name, but with different
signatures
Overriding
- A class replacing an ancestor's implementation of a method with an
implementation of it own
-
- Signature and return type must be the same
-
- Static methods can not be overridden
Hiding Fields
- Giving a field in a class the same name as a field in an ancestor hides the
ancestor's field
-
- The field exists, but can not be accessed by its short name
When invoking a method on an object, the actual type of the
object is used to determine which method implementation to use,
that is the method is determined dynamically
When accessing a field in an object, the declared type of the
reference is used to determine which field to use, that is the
field is determined statically
Why not the Same rule for Methods and Fields?
- The OO gods have decreed that overriding methods is good
- The OO gods have decreed that resolving overridden methods dynamically is
very good
- This is polymorphism, examples showing why this is good later
- Resolving hidden fields can not be done dynamically
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();
}
}
- Either resolve hidden fields statically or do not allow fields to be
hidden
Method vs. Field Access
class Parent {
public String name = "Parent";
public void print() {
System.out.println( "In Parent \t" + name );
}
}
class Child extends Parent {
public int name = 5;
public void print() {
System.out.println( "In Child \t" + (name * 2) );
}
}
class StaticVersesDynamic {
public static void main( String args[] ) {
Child prodigy = new Child();
prodigy.print(); //Prints In Child 10
Console.println( prodigy.name ); //Prints 5
Parent confused = new Child();
confused.print(); //Prints In Child 10
Console.println( confused.name ); //Prints Parent
Parent ok = new Parent();
ok.print(); //Prints In Parent Parent
Console.println( ok.name ); //Prints Parent
}
}
Fancy Accesses
class Parent {
private String name = "Parent";
public void print() {
System.out.println( "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( "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
Overloading, Overriding and Return Types
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
- Refers to the superclass of class that implements the method containing the
reference to super
-
- All references to super are resolved statically
class Parent {
String name = "Parent";
}
class Child extends Parent {
String name = "Child";
public void print() {
System.out.println( name );
System.out.println( super.name );
System.out.println( Parent.name );
}
}
class SuperMain {
public static void main( String args[] ) {
Child whoAmI = new Child();
whoAmI.print();
}
}
Output
Child
Parent
Parent
More Super
class Parent {
public String name = "Parent";
public void print() { System.out.println( "In Parent" ); }
}
class Child extends Parent {
public String name = "Child";
public void print() {
System.out.println( "In Child" );
}
public void superTest() {
System.out.println( "In Child test " + super.name );
super.print();
}
}
class GrandChild extends Child {
public String name = "GrandChild";
public void print() {
System.out.println( "In GrandChild" );
}
}
class SuperIsStatic {
public static void main( String args[] ) {
GrandChild whoAmI = new GrandChild();
whoAmI.superTest();
}
}
Output
In Child test Parent
In Parent
this
- references to fields via this are resolved statically
-
- references to methods via this are resolved dynamically
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();
}
}
Output
Parent
In Child Child
Parent
In Child Child
super.super and this.
class GrandParent {
public String bother = "GrandParent";
}
class Parent extends GrandParent {
public String bother = "Parent";
}
class Child extends Parent {
public String bother = "Child";
public void bother( String bother ) {
System.out.println( "bother " + bother );
System.out.println( "this.bother " + this.bother );
System.out.println( "super.bother " + super.bother );
// super.super is a compile error
//System.out.println( super.super.bother );
}
public static void main( String args[] ) {
Child plays = new Child();
plays.bother( "Hi" );
}
}
Output
bother Hi
this.bother Child
super.bother Parent
Static methods reference 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
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 void static main( String[] args ) {
Hija confundido = new Hija ();
confundido.foo();
}
}
Output
Madre bar
Protected Access and Inheritance
protected
- Accessible in the package that contains the class
- Accessible in all subclasses
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
- If the access is by a field access expression of the form super.ID, then
the access is permitted
-
- If the access is by a qualified name Q.Id, where Q is a type name, then the
access is permitted if and only if Q is S or a subclass of S.
-
- If the access is by a qualified name Q.Id, where Q is an expression then
the access is permitted if and only if the type of the expression Q is S or a
subclass of S
-
- If the access is by a field access expression E.Id, then the access is
permitted if and only if the type of E is S or a subclass of S
Package Access and Inheritance
No Explicit Access
- Accessible in the package that contains the class
-
- Not accessible outside the package that contains the class
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 and 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() throws IOException{ return 5;}; //OK
}
Rules Are Different for Fields
class Parent
{
protected int foo;
public int bar;
}
class Child extends Parent
{
public int foo; // OK
protected int bar; // OK
}
The final modifier provides:
- Security
-
- Performance optimizations
A class declared to be final, can not have any subclasses
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" );
}
}
class Parent {
public Parent() {
System.out.println( "In Parent" );
}
}
class Child extends Parent {
public Child() {
System.out.println( "In Child" );
}
public Child( String notUsed ) {
System.out.println( "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();
care = new Child( "Try Me" );
}
}
Output
Construct Parent
In Parent
Construct Child
In Parent
In Child
In Parent
In Child with argument
Calling Other 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
All classes inherit from Object
- class Parent {
-
- int size;
- }
Is
short hand for
- class Parent extends Object {
-
- int size;
- }
class Parent {
int size;
}
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()
- Creates a clone of the object.
equals(Object)
- Compares two Objects for equality.
- Uses "==" to test for equality
finalize()
- Code to perform when this object is garbage collected.
getClass()
- Returns the Class of this Object.
hashCode()
- Returns a hashcode for this Object.
toString()
- Returns a String that represents the value of this Object.
If a class needs a implementation of equals which differs from the default
"equals" the class should override both equals and hashCode
If two different objects satisfy the equals method, the hashCode should return
the same value for both objects
Dangerous and Advanced Material
import sdsu.io.Console;
class Foobar {
public String name = "Skip if this confuses you";
}
class DealingWithClasses {
public static void main( String[] arguments ) throws
ClassNotFoundException, IllegalAccessException,
InstantiationException {
Foobar example = new Foobar();
Console.println( example.getClass().getName() );
Console.println( example.getClass().getSuperclass() );
// Stop reading while it is still safe
// Creating an object from a string
example = (Foobar) Class.forName( "Foobar" ).newInstance();
String whichClass = "java.util.Vector";
Object interesting = Class.forName( whichClass ).newInstance();
Console.println( interesting.getClass().getName() );
whichClass = Console.readLine( "Enter a class name" );
interesting = Class.forName( whichClass ).newInstance();
Console.println( interesting.getClass().getName() );
}
}
Casting and Classes
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;
child = (Child) object;
uncle = (Uncle) object; //Runtime exception
}
}
Output
java.lang.ClassCastException: Child
at Casting.main(All.java:21)
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