Classes and Objects
Each pattern
describes a problem which occurs over and over again in our
environment, and then describes the core of the solution to that
problem, in such a way that you can use this solution a million times
over, without ever doing it in the same way twice.
- C. Alexander
Read about inheritance
in the online Java Tutorial.
Recall: Instance variables vs. local variables
- Instance variables are declared inside a
class, but not
inside a method
- Local variables are declared inside a
method, and they can
only be used inside the method
- Local variables must be initialized before
use, while
instance variables have a default initial value:
- numbers (double, float, int, etc) are 0
be default
- booleans are false by default
- object reference variables have a
default initial value of
null
Homework: before our
next class, review:
the static methods Integer.parseInt()
and Double.parseDouble()
An
Introduction
to Inheritance
Consider a program that involves different types of
animals: you
write the classes Dog, Cat,
and Bird.
As you write these classes, you will notice that they contain some
common instance variables:
And some common
methods:
Since some of the data and functionality for these classes overlap, we
may put common code in a parent class
or superclass.
For our program, we may want a parent class called Animal that
contains
the common instance variables and methods we described above.
Dog, Cat
and Bird
will be subclasses
of the Animal
Class, and they will inherit the instance variables and
methods in Animal.
The advantage of having the superclass, Animal,
is
that we save
ourselves the hassle of rewriting code over and over in the subclasses.
Code Re-use and Inheritance
In the real world, we categorize objects - starting with a general
category and then becoming more specific.
Example: Think about people - a general category. Suppose we
want a Java class that represents any person. What attributes does
every person have? What actions does every human being know how to do?
Person
|
name
age
SSN
|
talk
play
eat
|
In diagrams like this one, the class name goes in the first box, data
for the class appears in the second box, and behaviors go in the third
box.
Now consider a subcategory of Person - UTStudent.
If we also
want a Java class that is a blueprint for every UT student, some of the
data and behavior for any UTStudent
is already represented in the
Person
class. We don't want to reinvent the work we did for the
Person
class - so when we write the UTStudent
class, we will only think
about data and behavior that only applies to UT students, not all
people.
We will re-use the data and behavior in the Person
class by having our
UTStudent
class inherit from
Person.
UTStudent ("a kind of" Person):
attributes that did not appear in Person
class? UTEID, class schedule,
feesOwed
behavior that did not appear in Person
class? payFees, study, talk
about UT
Person
|
name
age
SSN
|
talk
play
|
UTStudent
|
UTEID
classSchedule
fees
|
study
talk (about UT)
|
Note:
- We don't repeat fields and methods from Person in UTStudent
- The subcategory's objects (UT students) may
perform some actions differently from the more general
category's
objects (people), e.g., talk()
How does inheritance look in Java?
public
class
Person {
private
String name;
private
int age;
private
String SSN;
public
void play() {
System.out.println("Out having fun...");
}
public
void talk() {
System.out.println("Hi!");
}
}
UTStudent
is a subclass of Person - it
automatically has all the data fields and methods from Person
class. We say that Person is
the superclass, or parent class, of UTStudent.
public
class
UTStudent extends Person {
//
don't repeat inherited data here - UTStudent objects have name,
// age,
SSN fields
private
String UTEID;
private
String[] classSchedule;
private
double fees;
public
void study() {
System.out.println("I'm in the lab writing Java code...");
}
public
void talk() {
System.out.println("Hi, Java spoken here...");
}
}
Now: Each UTStudent object
automatically...
- has name, SSN, age
instance fields
- has a play method
- can be treated as a Person
object in any program
- Person[]
family
= new Person[5];
- family[1] = new
UTStudent(...); // array entries can reference UTStudent
objects, since UTStudents
are also of type Person
- Notice that we re-wrote one of the methods (talk) that
was inherited from Person
inside the UTStudent class, to make its behavior more appropriate for
students. This is called overriding
an inherited method.
Inheritance
- inherit:
To receive all the data fields and methods from another class
- superclass,
or
parent class: the more
general class
- subclass,
or
child class: the class
that
inherits from the parent class; the subclass is a subcategory of the
parent class.
Syntax for a sub-class:
public class <childName> extends <parentName> {
// Data fields and methods not inherited from
parent go here
// Methods that we are overriding from the
parent class go here
}
Example:
public class
Lion extends Animal {
...
}
An Inheritance Example: Rectangle and Square
Squares
are
a subcategory of Rectangles.
A square is a rectangle with equal height and width.
Since a Square
"is a kind of" Rectangle, Square can
inherit from Rectangle.
If we wrote the Square
class from scratch, much of the code would duplicate code from our Rectangle
class. Instead, we will eliminate that redundancy by having Square
inherit that code from Rectangle.
public
class
Rectangle {
private
double length;
private
double width;
public
Rectangle(double len, double wth) {
length = len;
width = wth;
}
public
double getWidth() {
return width;
}
public
double getLength() {
return length;
}
public
double area() {
return length*width;
}
public
void setLength(double length) {
if(length >= 0) this.length = length;
}
public
void setWidth(double width) {
if(width >= 0) this.width = width;
}
public
String toString() {
// return String representation of rectangle
String repr = "Rectangle[width = " + width + ", length = "
+ length + "]";
return repr;
}
}
How are Square and Rectangle different?
Square
only needs one size parameter in its constructor:
Square sq = new
Square(2);
Rectangle rect = new Rectangle(4, 5);
Squares
should
print their string representation differently with toString():
"Square[side = 2]"
"Rectangle[length = 4, width = 5]"
Inheritance and the Square Class
A Square
is just a special Rectangle,
and a square automatically inherits:
- length and width fields
- area, toString,
getLength, getWidth methods
Note that in the child class, the superclass constructor
can be invoked using the keyword super.
public
class
Square extends Rectangle {
// initialize this Square's length, width to the constructor's
argument
public
Square(double size) {
super(size, size); // invoke superclass constructor
}
//
over-ride toString() method inherited from Rectangle
//
When toString() called on a Square object, this version executed
public
String toString() {
return "Square[side = " + this.getLength() + "]";
}
}
Note: To access the length of
the Square,
we must use this.getLength(),
not this.length,
since length
is a private field in the Rectangle class.
Object: The
Superclass of Every Java
Class
Every class automatically inherits
from class Object
- this means that every class extends Object and
can
use the methods in class Object.
The Object
class contains methods that you want every object to have. We've used
one of them briefly before.
Object's
methods
are very general: here are the most useful ones.
- String toString(); // returns a string
representation of the
object
- boolean equals(Object obj); // tests whether or
not this object
equals obj
- Object clone(); // makes a copy of this object
Since these methods are so general, it is a good idea to override them in your own classes.
Recall that we override a
method by
providing our own implementation of it.
Writing
the equals() method: Testing the Class Type of an Object
The instanceof
Operator
Syntax: <object> instanceof
<ClassName>
This operator returns true if the object is an instance of ClassName
and false otherwise.
Example:
if (x
instanceof Square)
{
Square xSqr = (Square) x;
...
}
Overriding Object's Methods: An Example
Write a Square
class
which overrides the toString(),
equals(),
and clone()
methods inherited from the Object
class. Then write a
SquareTest
class that tests the Square
class.