Contents [0/22] |
Overview of Today's Class [1/22] |
Homework Solution - HW3 [2/22] |
Homework Solution - HW4 [3/22] |
JUnit: Patterns [4/22] |
Patterns: Template Method Example (another one) [5/22] |
Patterns: The Command Pattern [6/22] |
Patterns: Command Example [7/22] |
Patterns: The Composite Pattern [8/22] |
Patterns: Composite Example [9/22] |
Inner Classes: Definitions and Static [10/22] |
Inner Classes: Non-static [11/22] |
Inner Classes: Local Inner Classes [12/22] |
Inner Classes: Anonymous Inner Classes [13/22] |
Java classes: The canonical form of classes [14/22] |
Java classes: 0-arg constructor [15/22] |
Java classes: Equality [16/22] |
Java classes: Hash Code [17/22] |
Java classes: clone() [18/22] |
Java classes: cloning implementation [19/22] |
Java classes: toString [20/22] |
Java classes: Serialization [21/22] |
Quiz Questions? [22/22] |
Overview of Today's Class [1/22] |
Homework Solutions (HW3 and HW4)
Composite and Command Patterns
Inner Class Idiom and Anonymous Inner Classes
The Canonical form of classes
Assertions in java
Design By Contract
Project Overview
Refactoring
Quiz questions
Homework Solution - HW3 [2/22] |
One way to do the homework assignment.
The files are here.
Homework Solution - HW4 [3/22] |
One way to do the homework assignment.
The TestCase (I wrote this one first) FlightTest.java.
The implementation. Flight.java.
One note on Comparable.... if compareTo isn't consistent with equals
JUnit: Patterns [4/22] |
JUnit is actually a great demonstration of a number of patterns from GoF. We will look at examples of Template Method(325), Composite (163) and the Command (233) patterns. JUnit also uses Adapter (139).
Patterns: Template Method Example (another one) [5/22] |
GenericClass: TestCase
ConcreteClass: VectorTest
public abstract class TestCase extends Assert implements Test { /** * Runs the bare test sequence. * @exception Throwable if any exception is thrown */ public void runBare() throws Throwable { setUp(); try { runTest(); } finally { tearDown(); } } /** * Sets up the fixture, for example, open a network connection. * This method is called before a test is executed. */ protected void setUp() throws Exception { } /** * Tears down the fixture, for example, close a network connection. * This method is called after a test is executed. */ protected void tearDown() throws Exception { } }
Patterns: The Command Pattern [6/22] |
Patterns: Command Example [7/22] |
Participants and Collaborators
public interface Test { /** * Runs a test and collects its result in a TestResult instance. */ public abstract void run(TestResult result); } public abstract class TestCase extends Assert implements Test { /** * Runs the test case and collects the results in TestResult. */ public void run(TestResult result) { result.run(this); } } public class TestRunner extends BaseTestRunner { public TestResult doRun(Test suite, boolean wait) { TestResult result= createTestResult(); result.addListener(fPrinter); long startTime= System.currentTimeMillis(); suite.run(result); long endTime= System.currentTimeMillis(); long runTime= endTime-startTime; fPrinter.print(result, runTime); pause(wait); return result; } }
Patterns: The Composite Pattern [8/22] |
Patterns: Composite Example [9/22] |
Participants and Collaborators
public interface Test { /** * Runs a test and collects its result in a TestResult instance. */ public abstract void run(TestResult result); } public abstract class TestCase extends Assert implements Test { /** * Runs the test case and collects the results in TestResult. */ public void run(TestResult result) { result.run(this); } } public class TestSuite implements Test { /** * Adds a test to the suite. */ public void addTest(Test test) { fTests.addElement(test); } /** * Runs the tests and collects their result in a TestResult. */ public void run(TestResult result) { for (Enumeration e= tests(); e.hasMoreElements(); ) { if (result.shouldStop() ) break; Test test= (Test)e.nextElement(); runTest(test, result); } } }
Inner Classes: Definitions and Static [10/22] |
In Java, classes and interfaces can be members of other classes and interfaces. These are considered nested classes and nested interfaces. They are part of the contract of their enclosing type.
Nested classes and interfaces can be assigned the same access as other members of a class.
The simplest nested class to talk about is a static nested class - technically called a top-level nested class (but this is a terribly confusing name). Static nested classes aren't inner classes.
public class BankAccount { private long number; private long balance; public static class Permissions { public boolean canDeposit, canWithdraw, canClose; } // ... }
The full name of this class is BankAccount.Permissions
.
static inner classes can be intantiated without creating an instance of the outer class. They make sense when used as a namespace mechanism more than anything. They can have any level of accessibility
Inner Classes: Non-static [11/22] |
When talking about non-static nested classes, we refer to them as inner classes. You need an instance of the outer class to instantiate the inner class.
For example:
public class BankAccount { private long number; private long balance; private Action lastAct; public class Action { private String act; private long amount; Action(String act, long amount) { this.act = act; this.amount = amount; } public String toString() { return number + ": " + act + " " + amount; } } public void deposit(long amount) { balance += amount; lastAct = new Action("deposit", amount); } public void withdraw(long amount) { balance -= amount; lastAct = new Action("withdraw", amount); } // ... }
the creation of the Action could be written more explicitly
as lastAct = this.new Action("deposit", amount);
. Outside the BankAccount class, this would look like
this:
BankAccount ba = new BankAccount(); Action act = ba.new Action("deposit", 4);
A nested class can use other members of the enclosing class without qualification (including private fields). A static nested class (last slide) can directly access only static members of the enclosing class.
The toString() method above could be written as return
BankAccount.this.number + ": " + act + " " + amount;
This is the way that you access the outer class from the inner
class if you need to explicitly accesss an enclosing class's
members.
It is recommended that you only nest classes one deep, but any level is allowed.
Inner classes can't have static members.
Inner Classes: Local Inner Classes [12/22] |
Inner classes can be created in code blocks, such as method bodies. These classes are not members of the class, so they can't be private, protected, public, static or final. They have access to the final variables of the code block as well as other members of the class, static and not-static. (think scope as to why this is the case)
Consider the Iterator interface (we talked about it last week and will look at it again tonight).
public interface Iterator { public boolean hasNext(); public Object next(); public void remove(); }This interface is a great candidate for a local inner class.
public static Iterator walthThrough(final Object[] objs) { // local inner class class Iter implements Iterator { private int pos = 0; public boolean hasNext() { return (pos < objs.length); } public Object next() throws NoSuchElementException { if(pos < objs.length) throw new NoSuchElementException(); return objs[pos++]; } } return new Iter(); }
Inner Classes: Anonymous Inner Classes [13/22] |
Anonymous classes can be written that implement an interface or extend another class. These classes are just defined at the same time they are created with new. They are perfect for times when you don't want the weight of a full class in your code
For example:
public static Iterator walthThrough(final Object[] objs) { // anonymous class return new Iterator() { private int pos = 0; public boolean hasNext() { return (pos < objs.length); } public Object next() throws NoSuchElementException { if(pos <= objs.length) throw new NoSuchElementException(); return ojs[pos++]; } }; }or:
Attr name = new Attr("Name") { public Object setValue(Object nv) { System.out.println("name set to " + nv); return super.setValue(nv); } };or:
goButton.addActionListener ( new ActionListener() { public void actionPerformed(ActionEvent e) { someMethod(); } } );
Java classes: The canonical form of classes [14/22] |
In order to behave properly in the Java environment, you need to be aware of some issues when you construct your classes.
Many of these issues will come up as you move into larger projects and programs
We use the term the canonical form to describe classes that will be well behaved when manipulated by the java run-time environment and other classes, such as the collections framework
When you design classes for general use, you should consider the following:
Java classes: 0-arg constructor [15/22] |
A zero arg constructor is needed by any code that calls the
new
operator on your class.
It is also needed by code that dynamically instantiates your class. This can be done as follows:
Class yourClass = Class.forName("se450.mwright1.hw4.Student"); Student student = (Student)yourClass.newInstance();
This kind of code is used in various application servers as well
JavaBeans are classes that need a 0-arg constructor to work properly by other Java classes as well
Java classes: Equality [16/22] |
A good equals means the following is true. It is:
Java classes: Hash Code [17/22] |
The Hashcode is used in collections classes such as HashSet
and HashMap
. If the equals method is overridden, it is
necessary to override the hashCode method.
In general, it can be done as follows:
The combination can be done as follows:
hash = hash << n | c
where n is an arbitrary integer, say 8.hash = hash * p + c
where p is
a prime number, say 37The key is to make sure that the hashcode is always the same for equal values. If it isn't, a value can be put in a hashtable, and then never be able to be retrieved
Java classes: clone() [18/22] |
We have looked at the equals, toString, and hashCode methods
that are all inherited from Object
. We now need to
look at the clone
method.
It is analogous to the Copy constructor in C++
The intent is that
x.clone() != x x.clone().getClass() == x.getClass() x.clone().equals(x)
The last intent is not a requirement, but is generally true.
By convention, you obtain the returned object by calling
super.clone()
. If all superclasses do this,
then x.clone().getClass() == x.getClass()
will
be true.
Object
doesn't implement Cloneable
itself
The Cloneable
interface is meant to be a
mixin interface, however it doesn't do a very good job of this.
The Cloneable
interface doesn't contain the clone
method, so classes can't be guaranteed to call this method (it may not
have the right visibility). But since it is in pretty wide use,
we should understand it. We will look at a design pattern, the
Prototype in a few weeks that is a better way to do this.
However, you need to be concerned about the deep vs. shallow copy problem. If a there are mutable objects that are members of the object being cloned, they may need to be copied during the clone operation, and references to the objects changed to point to the new objects.
To clone you need:
Cloneable
interface
(note the misspelling)
Object.clone
method (or your superclass's
clone method)
CloneNotSupportedException
, to signal the
clone method shouldn't have been called.
You can:
Object.clone
method, but it is
protected, so it is not of much use for other classes to use.
Java classes: cloning implementation [19/22] |
Simple:
public class MyClass extends HerClass implements Clonable { public Object clone throws CloneNotSupportedException { return super.clone(); } // ... }But what about
public class IntegerStack implements Clonable { private int[] buffer; private int top; public IntegerStack(int maxContents) { buffer = new int[maxContents]; top = -1; } public void push(int val) { buffer[++top] = val; } public int pop() { return buffer[top--]; } }
What does the default clone method do?
Fix this by overriding clone
public Object clone() { try { IntegerStack nObj = (IntegerStack)super.clone(); nObj.buffer = (int[])buffer.clone(); return nObj; } catch (CloneNotSupportedException e) { // cannot happen since we and arrays support clone throw new Error(e.toString()); } }
To disallow cloning:
public final Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); }
There are alternatives:
public Foo(Foo foo)
Java classes: toString [20/22] |
the toString
method is invoked whenever you use
the object in System.out.println
. A good String representation
of your object makes debugging it much easier than the standard
String representation. A good rule of thumb is to include significant
fields of an object (like those in the equals
method
in your toString
method.
Avoid this, of course, if
they will make it confusing or violate encapsulation. Never put
data in your toString
method that users may come to
depend on because you don't provide an accessor to it.
Java classes: Serialization [21/22] |
Java supports serialization for any object that implements
the java.io.Serializable
interface.
// write.java import java.io.*; public class write { public static void main(String args[]) { try { FileOutputStream fos = new FileOutputStream("file.out"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(new Test("testing", 37)); oos.flush(); fos.close(); } catch (Throwable e) { System.err.println(e); } } } // read.java import java.io.*; public class read { public static void main(String args[]) { Test testobj = null; try { FileInputStream fis = new FileInputStream("file.out"); ObjectInputStream ois = new ObjectInputStream(fis); testobj = (Test)ois.readObject(); fis.close(); } catch (Throwable e) { System.err.println(e); } System.out.println(testobj.str); System.out.println(testobj.ivalue); } } // Test.java public class Test implements java.io.Serializable { public String str; public transient int ivalue; public Test(String s, int i) { str = s; ivalue = i; }
Serialization is used by many Java APIs, especially RMI and many J2EE technologies
You can control serialization with more detail using the
java.io.Externalizable
interface.
package java.io; public interface Externalizable extends Serializable { public void writeExternal(ObjectOutput out) throws IOException; public void readExternal(ObjectInput in) throws IOException, java.lang.ClassNotFoundException; }
Control whether fields get persisted using transient
.
You must be aware that an object that is restored from persistence will
need its transient fields initialized.
You may find it very useful in your project
Quiz Questions? [22/22] |
Any questions about the quiz?
Revised: 2003/2/3