0. Assignment: Please read chapters 8 on interfaces and 10 on exceptions (we'll come back and pick up chapter 9 shortly). Please read all of chapter 8, but focus on the sections about interfaces (at this point a "read-only" understanding of inner classes will suffice).
1. Inheritance and polymorphism revisited.
public abstract double area();A class containing an abstract method is itself abstract--must be marked with the keyword "abstract" in the header of its definition, and can't be directly instantiated (you can't do "new" on an abstract class).
// Sketch of a set of classes to represent a game show.
// We start with prizes. A Prize--any Prize--is taken to have a dollar value:
// we represent this via a data member, and write a corresponding accessor
// method. We'll also say that it might be useful in the context of the game for
// contestants to be able to ask for hints about what's behind door #2, so
// we'll have a hint() method. The hint() method will be specific to a type of
// prize, so will need to be defined in a prize subclass. We indicate that
// subclasses of Prize must define hint() by marking hint() (and Prize
// itself) as "abstract". Notice the comments below in main() of MakeADeal
// about the consequences of omitting the reference to hint() in Prize.
public abstract class Prize {
protected double monetaryValue;
public double getMonetaryValue() { return monetaryValue; }
// need something like this to make the refs to hint() in
// Driver legal:
// public String hint() { return ""; }
// better (with abstract added too to the class):
public abstract String hint();
}
public class Goat extends Prize {
public Goat(double d) { monetaryValue = d; }
public String hint() { return "Gimme tin"; }
}
public class TripToBakersfield extends Prize {
public TripToBakersfield(double val) { monetaryValue = val; }
public String hint() { return "First prize, one week. Second prize, two weeks."; }
}
public class MakeADeal {
public static void main(String[] args) {
// Do a little testing of the Prize classes. Make a Prize array and
// fill it with instances of the subclasses
Prize[] door = new Prize[2];
door[0] = new TripToBakersfield(2.98);
door[1] = new Goat(5000.00);
// Iterate through the array, calling getMonetaryValue() on the objects
// we encounter. The version of getMonetaryValue() that's invoked is
// the one defined in Prize.
for (int i = 0; i < door.length; i++) {
System.out.println("Door #" + i + ": " + door[i].getMonetaryValue());
}
// Iterate through the array, calling hint() on the objects we encounter.
// The versions of hint() that are invoked are the ones defined in the
// subclasses. Nonetheless, hint() needs to be defined in Prize or the
// code won't compile (the compiler says "I don't know for sure that this
// array will actually hold objects that know how to do that").
for (int i = 0; i < door.length; i++) {
System.out.println("Door #" + i + ": " + door[i].hint());
}
}
}
2. Interfaces.
// You and I agree to cooperate on a project. You're going to be creating
// objects that do all kinds of interesting things, and you'd like me to write
// some code that'll do tricky mathematical tests on those objects. We
// enshrine our agreement, in part, in the definition of an abstract class:
// the objects you create will be instances of subclasses of TheContract;
// therefore I know I'll always be able to do getVal() on TheContract
// instances to get the object value I need to do my tests on.
public abstract class TheContract {
public abstract int getVal();
}
// So I go off to scribble, and the first fruits of my work is a static method in
// Me called isZero that says whether the value of your object is zero.
// Observe the "coding to the base class": isZero() is defined to take a
// TheContract param, so will accept instances of any subclass of
// TheContract.
public class Me {
public static boolean isZero(TheContract y) { return y.getVal() == 0; }
}
// And you then go off and write code using mine:
public class You extends TheContract {
// The usual: data member, accessors, constructor
protected int val;
public void setVal(int v) { val = v; }
public int getVal() { return val; }
public You(int v) { val = v; }
// Let's see if it works: make an instance of You (which descends from
// TheContract), setting an initial value; then run it through isZero() and
// see what comes back
public static void main(String[] args) {
You y1 = new You(10);
if (Me.isZero(y1))
System.out.println("Zero");
else
System.out.println("Non-zero");
}
}
// And everybody lives happily ever after.
//
// Many, many years later you're working on a project where you're
// creating objects that do all kinds of interesting things, and you need
// to do some tricky mathematical tests on them. You still have my .class
// files (but, alas, not the source), and you think you're in business.
// Unfortunately, for whatever reason, You2 can't extend TheContract
// (maybe it has to extend some other class), and this means isZero()
// is unusable, since it requires a TheContract descendant as a param.
//
// The code that follows is essentially identical to that for You, with the
// interpolation of comments at the point where the compiler runs into
// trouble.
public class You2 {
protected int val;
public void setVal(int v) { val = v; }
public int getVal() { return val; }
public You2(int v) { val = v; }
public static void main(String[] args) {
You2 y = new You2(10);
/* Won't compile--the compiler says:
"You2.java:9: isZero(TheContract) in Me cannot be applied to (You2)
if (Me.isZero(y))"
if (Me.isZero(y))
System.out.println("Zero");
else
System.out.println("Non-zero");
*/
}
}
// You and I agree to cooperate on a project. Etc., etc.
//
// Observe that an interface definition closely resembles a class
// one. All method references in interface definitions must be blank
// (i.e., body-less).
public interface Evaluate {
public int getVal();
}
// As before, except I specify the parameter to my method as being
// an Evaluate (some thing I can interrogate via getVal())
public class Me {
public static boolean isZero(Evaluate e) { return e.getVal() == 0; }
}
// As before, except you say "implements Evaluate" instead of "extends
// TheContract". In the event a class implements multiple interfaces,
// comma-delimit the list of interfaces you're implementing
public class You implements Evaluate {
protected int val;
public void setVal(int v) { val = v; }
public int getVal() { return val; }
public You(int v) { val = v; }
public static void main(String[] args) {
You y1 = new You(10);
if (Me.isZero(y1))
System.out.println("Zero");
else
System.out.println("Non-zero");
}
}
// As before, except, as for You, you say "implements Evaluate".
// And this will compile (because you're handing isZero() an instance
// of a class that implements Evaluate, and isZero() will take an instance
// of any such class.
public class You2 implements Evaluate {
protected int val;
public void setVal(int v) { val = v; }
public int getVal() { return val; }
public You2(int v) { val = v; }
public static void main(String[] args) {
You2 y = new You2(10);
if (Me.isZero(y))
System.out.println("Zero");
else
System.out.println("Non-zero");
}
}
3. Exceptions.
try {
...
}
catch (Exception e) {
...
}
The "try" block contains your "normal code", the code you'd really like
to execute. Among other things it contains the code that may
generate an exception. If no exception occurs, the try block executes
sequentially and the catch block is never reached. If an exception does
happen, control immediately jumps to the "catch" code. The parameter
to the catch block indicates what kind of exception the block is meant
to handle. It's possible to have multiple catch blocks, each trapping
a different type of exception (observe that, since Exception is the
ancestor class for (almost) all exceptions, a catch block with an
Exception parameter will catch any exception; so if your code can
generate different kinds of exceptions, and if you're interested in
treating them differently, you have to put the catch block with the
Exception parameter last in the list of catch blocks--or, stated
differently, catch blocks should be ordered from most-specific to
most general).
public double RiskyProposition() throws ReallyBadException {
...
}
In this case, if an exception is generated in RiskyProposition(), it's
passed up the method-call chain, in the first instance to the method
from which RiskyProposition was called.
public class ReallyBadException extends Exception {
...
}
...
if (trainRanOffTheTracks == true) {
throw new ReallyBadException();
}
// Exceptions1.java
//
// Fetches byte length of a URL using some built-in Java networking
// classes. Demos exception-catching.
// need this for URL and URLConnection
import java.net.*;
// need this for the IOException guy
import java.io.*;
public class Exceptions1 {
// Everything's going to happen in main(). We make a URL object from
// a String; make a URLConnection object from the URL object--where
// the URLConnection object models the idea of a live connection to the
// server holding the resource pointed to by the URL; then call a method
// of URLConnection to fetch the byte length of the resource. We have to
// wrap all this in a "try" because the creation of the URL and
// URLConnection objects can fail and generate exceptions.
public static void main(String[] args) {
try {
String s = "http://www.microsoft.com";
// Instantiating URL can generate a MalformedURLException (if, e.g.,
// the URL you pass in commits a crime against URL syntax)
URL u = new URL(s);
// The openConnection() method of URL is supposed to return a
// a new URLConnection object. It can however fail and generate a
// IOException.
URLConnection uc = u.openConnection();
System.out.println("Length of " + s + " is " + uc.getContentLength());
}
catch (MalformedURLException m) {
System.out.println("Damn, bad URL");
}
catch (IOException i) {
System.out.println("Damn, bad io");
}
}
}
// Exceptions2.java
//
// Ostensible purpose of the program remains the same as
// before--to determine the byte length of a URL. In this case we
// move the possibly-exception-generating code into a separate
// method to demonstrate the use of "throws".
import java.net.*;
import java.io.*;
public class Exceptions2 {
// main() will call getTheLength(), which does all the work--
// sets up the URLConnection object, makes it issue an HTTP
// request, returns the result. An exception may come flying out
// somewhere along the line, but in this case it's not handled
// locally. Instead, the "throws" clause specifies that the
// exception should be passed on to the method from which
// getTheLength() was called, i.e., main().
public static int getTheLength(String s) throws Exception {
URL u = new URL(s);
URLConnection uc = u.openConnection();
return uc.getContentLength();
}
public static void main(String[] args) {
// All we really want to do in main() is to call getTheLength(),
// capture the result, and print it. But main() now calls a method
// that can generate an exception, so it's faced with the standard
// exception-handling choice: either handle the exception
// yourself (which is what we do here), or do another "throws".
try {
int len = getTheLength("http://www.hp.com");
System.out.println("Length is: " + len);
}
catch (MalformedURLException m) {
System.out.println("Damn, bad URL");
}
catch (IOException i) {
System.out.println("Damn, bad io");
}
catch (Exception e) {
System.out.println("Trouble ahead");
}
}
}
// Exceptions3.java
//
// It's byte length again, but this time we demo user-defined exceptions.
// The idea is that if the URL has certain characteristics we won't honor
// the request (of course this is pretty artificial in a program in which both
// the URL and the search string are hard-coded, but you can imagine
// different scenarios...)
import java.net.*;
import java.io.*;
// Define our own exception class. We can throw more in here if we
// like but in general it doesn't take much...
class AustralianException extends Exception { }
public class Exceptions3 {
public static void main(String[] args) {
// This version has the same basic structure as the previous one: main()
// calls a method that may throw an exception, so we have to have
// a try-catch here
try {
int len = doTheWork("http://www.smh.com.au");
System.out.println("Length is: " + len);
}
// Notice that this catch has to precede the next one or (since the
// one defined for Exception will catch any exception) it'll never be
// called.
catch (AustralianException pte) {
System.out.println("Sorry, mate, I don't want to do that");
}
catch (Exception e) {
System.out.println("Fup");
// "catch" isn't just about printing diagnostics--sometimes you have to
// give up the ghost
e.printStackTrace();
System.exit(1);
}
}
public static int doTheWork(String s) throws Exception {
// I'm using a String method to check for the occurrence of the quoted
// substring in s
if (s.indexOf(".au") > -1) {
throw new AustralianException();
}
// Else do what we did in previous versions
else {
URL u = new URL(s);
URLConnection uc = u.openConnection();
return uc.getContentLength();
}
}
}