Transcript Lecture 4
CS503: Fourth Lecture, Fall 2008
Advanced OOP and Lab.
Michael Barnathan
Here’s what we’ll be learning:
• Object Oriented Programming:
–
–
–
–
–
–
Inheritance.
Polymorphism.
Interfaces and implementations.
Upcasting and downcasting.
Abstract and final classes.
Use of generics.
• Lab:
– Built-in sorting.
– The Vector class.
• We’ll do recursion next time.
Inheritance
• Classes can “inherit” from each other.
• The “derived” (child) class automatically starts out with all
of the “base class”’s (parent’s) properties and methods.
• You can then “override” methods by redefining them in the
derived class.
• Inheritance is properly used when one class “is a” ‘nother
(“is a” is standard terminology).
– For example, a Truck “is a” Vehicle.
• Every class in Java inherits automatically from the Object
class. This is the only example of multiple inheritance
permissible in Java.
– If you don’t know what multiple inheritance is, be thankful.
Inheritance in Java
public class Vehicle {
//Base class.
public void go() { System.out.println(“Vroom!”); }
public void honk() { System.out.println(“Beep!”); }
}
public class Truck extends Vehicle {
//Derived class.
public void honk() { System.out.println(“HONK!”); }
}
Truck t = new Truck();
t.go();
System.out.println(t.honk());
//Valid. Trucks inherit go().
//“HONK!”, not “Beep!”
Polymorphism
• The primary use of inheritance is for
“polymorphism” (poly = many, morphos = forms).
• This is the practice of using a reference to the
base class with an object of a derived class.
• Any overridden methods called on the reference
will exhibit the behavior of the derived class:
Vehicle v = new Truck();
//Valid!
v.honk();
//“HONK!”
Polymorphism
• The real power of this is apparent when you use it in an
array or other data structure.
• That’s because you can automatically exhibit different
behaviors depending on what each object is:
Vehicle[] cars = new Vehicle[2];
cars[0] = new Vehicle();
cars[1] = new Truck();
for (int i = 0; i < cars.length; i++)
cars[i].honk();
//“Beep! HONK!”
Wait!
• “Ah, but arrays are homogenous!”, you say.
• Yes, and we’re perfectly fine.
• They all contain Vehicle references.
– You’re storing references in the array, not objects.
• Everything inherits from Object…
– So yeah, you can store Objects in arrays.
– And thereby store any combination of classes!
• There’s a catch to this:
– You can only call base class methods through a base class reference.
– The behavior will be that of the derived class.
– But the choice of methods is that of the base class.
• Bummer, but it makes sense:
– You have no idea what those Objects really are, so all you can do is call things
common to every Object: equals(), clone(), toString(), etc.
– Otherwise you might call a function that doesn’t exist.
Downcasting
• “But I do know what I just put into that array!”, you say.
• “Yeah, yeah, tell it to the compiler”, I respond.
• So you did:
Object[] arr = new Object[2];
arr[0] = new Truck();
arr[1] = new Hovercraft();
arr[0].honk();
//Error, arr[0] is an Object.
((Truck) arr[0]).honk(); //Now it works; we told Java it was really a Truck.
• This is called a downcast, because you are casting down the inheritance
hierarchy.
– Object is at the top, of course.
– Vehicle is right below it (along with every other base class).
– Truck is below Vehicle because it inherits from Vehicle.
• Make sure that you downcast into a type that the variable is compatible
with (either the derived type or a base class above it), or Java will throw
an exception. You can’t cast that Hovercraft to a Truck.
Upcasting
• And you can go the other way too.
• This is usually done implicitly by Java.
– e.g. Object o = new Truck();
• But you can also do it explicitly if you wish:
– Object o = (Object) new Truck();
• You can’t access overridden base class
methods this way, though:
– ((Vehicle) new Truck()).honk(); //”HONK!”
– The object is still a truck, so you’re still getting a
truck honk.
“Super”
• There is only one way to access overridden base
class methods in Java: “super”.
• “super” refers to the class you inherit from.
• Inside of the Truck class, super() refers to
vehicle…
• And if you were to call super.honk() somewhere
inside of Truck, it would “Beep!”
• This is commonly used to call a base class’
constructor in the derived class’ constructor.
– If you do this, make sure it’s the first statement.
Interfaces and Implementations
• Let’s say you’re more interested in what a class can do
rather than what it is.
• Inheritance is not appropriate here.
• What you need is an interface.
– In Java, an interface is a “contract”.
– It defines some methods.
– In exchange for implementing those methods, Java will let
you call your object using a reference to the interface type.
– That’s because it knows that the methods in the interface
are implemented and it can safely call them.
– This has the same benefits as polymorphism.
Defining an Interface
• Interfaces in Java are defined like this:
interface Honkable {
public void honk();
//Other functions can go here too.
//But you just define them; you don’t
//implement them here.
}
Implementing an Interface
• Vehicle could then implement Honkable:
public class Vehicle implements Honkable {
//honk() must be implemented now to compile.
public void honk() { … }
}
• Unlike inheritance, you can implement more than
one interface in Java. Just separate the interface
names with commas.
The Reward
• You can now do this:
Honkable[] h = new Honkable[2];
h[0] = new Vehicle();
h[1] = new FrenchHorn();
for (int i = 0; i < h.length; i++)
h[i].honk(); //All things Honkable have this.
Abstract Classes
• A class is abstract if it defers implementing one or
more methods to a derived class.
• The unimplemented methods must be preceded
by the keyword abstract.
• You can also precede the class definition itself
with the abstract keyword.
• Either way, the end result is that people cannot
create objects of your abstract class. They must
create objects of non-abstract derived classes.
Abstract Classes
•
•
•
So why are they useful, then?
Because you can define common behavior to a category of things even if there’s no common
implementation of that behavior.
Example:
public abstract class Shape {
//There’s no area of a “shape”, but it makes sense to talk about shapes having areas.
public abstract double area();
}
public class Square extends Shape {
private double sidelen;
public double area() { return sidelen * sidelen; }
}
public class Triangle extends Shape {
private double base;
private double height;
public double area() { return .(base * height) / 2; }
}
//Squares have an area.
//Triangles have an area.
Final Classes
• Inheritance is great, but sometimes you don’t
want people deriving from your classes.
• No problem; just declare the class final.
public final class Planet { … }
//Error - sorry, Pluto.
public class Planetoid extends Planet { … }
Using Generics
• Generic classes are sort of complex to define in
Java, though not beyond your current knowledge.
• We’ll get to them some other time.
• But using them is simple:
Vector<String> vstr = new Vector<String>();
vstr.add(new String(“Foobar”));
//Works.
vstr.add(new Truck());
//Nope.
Lab
• 10 minute break, then we’ll meet in the lab.
Abstractions
• To the lab!
• The lesson:
– Abstraction is the root of intuitive understanding
of complex systems. Your ability to use the
Internet without understanding everything down
to how the glass in the fiber optic cables was
made is a testament to this. Abstraction is the
only way to build a complex system of any sort.
• Next class: Recursion.
– Abstraction might help you there, too.