This tutorial consists of several exercises on using anonymous inner classes as "helper" objects in the visitor pattern. In most cases, the main visitor only needs to create the helper visitor on-the-fly and uses it internally. It is most convenient to make the helper visitor as an anonymous inner object of the main visitor. This way, the helper visitor is known only to the outer main visitor, thus enforcing information hiding. Also since an inner object has access to all the internals (i.e. fields, methods, other inner objects, etc.) of the outer object, in many cases we need not pass any parameter to the anonymous inner visitor. The drawback is sometime the code may be hard to read.
When an inner object is created inside of a method, it has access to all the local variables that are final. By local variables, we mean both the parameters of the method and the variables that are declared locally. We can add the key word final to any parameter of a method and not change the method's signature. For example, a concrete visitor of an LRStruct may have its nonEmptyCase method declared as Object nonEmptyCase(final LRStruct host, Object inp). Any inner object created inside of this method can refer to (but can not re-assign) the host parameter.
To help you familiarize with writing anonymous inner classes, I re-use some of the exercises you have already done and reformulate them using inner classes.
For supplemental information on inner classes, see the Java Resources site.
First copy the two jar files, schemeFW.jar and lrs.jar, from the link http://www.owlnet.rice.edu/~comp212/01-fall/jar, to your lab 07 directory.
By putting them in your class path, you can import and use the schemeFW and lrs packages. These files are automatically in your class path if you put them in the same directory of the code you are trying to compile. When you compile your code, you can also specify where to look for the jar files:
(Below is an example for Sun Solaris)
javac -classpath .:full-jar-file-names your-java-source-code.Additonal jar files can be specified by separating them with colons (Solaris) or a semicolon (Windows) above.
To display the content of a jar file, enter the command: jar -tf jar-file-name. You can also use StructureBuilder to display the class diagrams for jar files.
To run your program using the code in the jar file, you need to override the classpath in use to include the jar file: (Solaris example)
java -classpath .:full-jar-file-names your-java-class-file.As with javac, additional jar files can be specified by separating them with colons (Solaris) or semicolons (Windows) above.
LastElement: finds and returns
the last element of the IList host.
Uses a named helper visitor called GetLastHelp. |
LastElt: finds and returns the
last element of the IList host.
Uses an anonymous local inner class helper visitor. |
---|---|
package schemeFW.visitor; import schemeFW.*; public class LastElement implements IListAlgo { public static final LastElement Singleton = new LastElement (); private LastElement () { } public Object emptyCase(IEmptyList host, Object input) { throw new IllegalArgumentException ("Empty list has no data!"); } public Object nonEmptyCase(INEList host, Object input) { return host.getRest ().execute(GetLastHelp.Singleton, host.getFirst ()); } } |
package
schemeFW.visitor; import schemeFW.*; public class LastElt implements IListAlgo { public static final LastElt Singleton = new LastElt (); private LastElt () { } public Object emptyCase(IEmptyList host, Object input) { throw new IllegalArgumentException ("Empty list has no data!"); } public Object nonEmptyCase(INEList host, Object input) { return host.getRest ().execute(new IListAlgo() { public Object emptyCase(IEmptyList h, Object inp) { // TO DO } public Object nonEmptyCase(INEList h, Object inp) { // TO DO } } // end of anonymous local inner class , host.getFirst ()); // host's first is passed to the helper as input. } } |
package schemeFW.visitor; import schemeFW.*; public class GetLastHelp implements IListAlgo { public static final GetLastHelp Singleton = new GetLastHelp (); private GetLastHelp () { } public Object emptyCase(IEmptyList host, Object input) { return input; } public Object nonEmptyCase(INEList host, Object input) { return host.getRest().execute(this, host.getFirst()); } } |
|
NOTE: Study the differences between this version of
reversing an immutable list with that
of a mutable list (LRSTruct) given in class.
Reverse: builds the reverse of
the IList host.
Uses a named helper visitor called RevHelp. |
Invert: builds the reverse of
the IList host.
Uses an anonymous local inner class helper visitor. |
---|---|
package schemeFW.visitor; import schemeFW.*; public class Reverse implements IListAlgo { public static final Reverse Singleton = new Reverse(); private Reverse() { } public Object emptyCase(IEmptyList host, Object input) { return host; } public Object nonEmptyCase(INEList host, Object input) { IList accRev = new NEList(host.getFirst(), EmptyList.Singleton); return host.getRest().execute(RevHelp.Singleton, accRev); } } |
package
schemeFW.visitor; import schemeFW.*; public class Invert implements IListAlgo { public static final Invert Singleton = new Invert(); private Invert() { } public Object emptyCase(IEmptyList host, Object input) { return host; } public Object nonEmptyCase(INEList host, Object input) { IList accRev = new NEList(host.getFirst(), EmptyList.Singleton); return host.getRest ().execute(new IListAlgo() { public Object emptyCase(IEmptyList h, Object inp) { // TO DO } public Object nonEmptyCase(INEList h, Object inp) { // TO DO } } // end of anonymous local inner class , accRev); // accumulated reverse is passed to the helper as input. } } |
package schemeFW.visitor; import schemeFW.*; class RevHelp implements IListAlgo { public static final RevHelp Singleton = new RevHelp(); private RevHelp() { } public Object emptyCase(IEmptyList host, Object input) { return input; } public Object nonEmptyCase(INEList host, Object input) { input = new NEList(host.getFirst(), (IList)input); return host.getRest().execute (this, input); } } |
|
RemMin: removes the minimum
from the LRStruct host.
Uses a named helper visitor called RemMinHelp. |
RemoveMin: removes the minimum
from the LRStruct host.
Uses an anonymous local inner class helper visitor. |
---|---|
package lrs.visitor; import lrs.*; public class RemMin implements IAlgo { public static final RemMin Singleton = new RemMin (); private RemMin() { } public Object emptyCase(LRStruct host, Object input) { throw new java.util.NoSuchElementException ("host is Empty."); } public Object nonEmptyCase(LRStruct host, Object input) { return host.getRest ().execute(RemMinHelp.Singleton, host); } } |
package lrs.visitor; import lrs.*; public class RemoveMin implements IAlgo { public static final RemoveMin Singleton = new RemoveMin (); private RemoveMin () { } public Object emptyCase(LRStruct host, Object input) { throw new java.util.NoSuchElementException ("host is Empty."); } public Object nonEmptyCase(LRStruct host, Object input) { // passes itself to the rest and asks the rest to "help" // find, remove, and return the min. return host.getRest ().execute(new IAlgo() { public Object emptyCase(LRStruct h, Object inp) { // TO DO: note that inp's first is the min; remove it! } public Object nonEmptyCase(LRStruct h, Object inp) { // TO DO: compare inp's first with h's first to find out which // LRStruct has the min, then pass the one with the min down // to the rest of h to help find the min and remove it! } } // end of anonymous local inner class , host); // host's first is the current minimum. } } |
package lrs.visitor; import lrs.*; public class RemMinHelp implements IAlgo { public static final RemMinHelp Singleton = new RemMinHelp(); private RemMinHelp() { } public Object emptyCase(LRStruct host, Object input) { // TO DO: note that input's first is the min; remove it! } public Object nonEmptyCase(LRStruct host, Object input) { // TO DO: compare inp's first with h's first to find out which // LRStruct has the min, then pass the one with the min down // to the rest of h to help find the min and remove it! } } |
|
Consider the following Counter class and associated visitor algorithm:
// ------------------------------------------------------------------------------
package counter; public class Counter { int _value = 0; private abstract class AState { public abstract Counter decrement(); public abstract Object execute(ICounterAlgo algo, Object inp); } private AState nonZeroState = new AState() { public Counter decrement(){ if(--_value == 0) aState = zeroState; return Counter.this; } public Object execute(ICounterAlgo algo, Object inp) { return algo.nonZeroCase(Counter.this, inp); } }; private AState zeroState = new AState() { public Counter decrement(){ return Counter.this; } public Object execute(ICounterAlgo algo, Object inp) { return algo.zeroCase(Counter.this, inp); } }; private AState aState = nonZeroState; public Counter(int value) { // value >= 0 _value= value; if(_value>0) aState = nonZeroState; else aState = zeroState; } public Counter decrement() { return aState.decrement(); } public Object execute(ICounterAlgo algo, Object inp) { return aState.execute(algo, inp); } } // ------------------------------------------------------------------------------
package counter; interface ICounterAlgo { public Object zeroCase(Counter host, Object inp); public Object nonZeroCase(Counter host, Object inp); } // ------------------------------------------------------------------------------ Using the above classes, write a visitor to LRStruct that will return the n'th element of a the list or null if n >= length of the list. (n=0 should return the first element of the list)
No if statements are necessary!! Any method will only have one line of code!
Get the jar file for the above code here.
D. X. Nguyen and S. B. Wong, Oct. 07, 2001 dxnguyen@cs.rice.edu, swong@cs.rice.edu