Comp 212 Lab 07: Anonymous Inner Classes


Introduction

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.


jar Files

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.


From Named Helpers to Anonymous Helpers

  1. In the table below, the left column shows the code for a schemeFW.IList visitor that finds and returns the last element of an IList host, using a named helper visitor.  The comments are elided in order to bring out the similarity in code structures. The right column shows a skeleton of a visitor that computes the same thing but uses an anonymous local inner class instead.  In this example, besides the renaming of the parameters, the code of the anonymous (local) inner class is identical to that of the named helper visitor, GetLastHelp.  Fill in the blanks!

    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());
        }
    }

     


  2. In the table below, the left column shows the code for a schemeFW.IList visitor that builds the reverse of an IList host, using a named helper visitor.  The comments are elided in order to bring out the similarity in code structures. The right column shows a skeleton of a visitor that computes the same thing but uses an anonymous local inner class instead.  In this example, besides the renaming of the parameters, the code of the anonymous (local) inner class is identical to that of the named helper visitor, RevHelp.  Fill in the blanks!

    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);
        }
    }

     


  3. Assume a LRStruct host contains Integer objects.  Write a visitor called RemMin to find, remove, and returns the minimum Integer object in the host.  Use anonymous inner classes as helper visitors.  The table below contains skeletons for both the named version and the anonymous version.  Do both.


    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!

        }
    }


     




  4. Assume a LRStruct host contains Integer objects.  Write a visitor called SwapMinWithFirst to find the minimum Integer object in the host, swap the content of the first node in the host with it, and return it.   Use anonymous inner classes as helper visitors.  No more skeletons!  You are on your own...

Optional for those who want to really get nuts:

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