Comp 212 Lab 04: Anonymous Inner Classes


Introduction

This tutorial consists of several exercises on using anonymous inner classes as "helper" objects in writing list algorithms as IListAlgo..  In most cases, the main (a.k.a. master) algorithm only needs to create the helper algorithm on-the-fly and uses it internally.  It is most convenient to make the helper alogorithm as an anonymous inner object of the main algorithm.  This way, the helper algorithmis known only to the outer main algorithm, 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 algorithm.  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 algorithm of an IList may have its nonEmptyCase method declared as Object nonEmptyCase(final INEList 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 File

In order to compile and test the code for the exercises, first copy the jar file, listFW.jar, from the link http://www.owlnet.rice.edu/~comp212/02-fall/lectures/04/listJar/ to your lab 04 directory. It contains the archived (compressed) classes for the immutable list framework listFW.IList and a concrete IList factory, CompositeListFactory.

By putting them in your class path, you can import and use the listFW 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 listFW.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.  Local inner class are defined inside of a method of a class.  It has access to all final local variables in this method as well as all the fields and method of the outer object.  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 listFW.visitor;
    import listFW.*;

    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 listFW.visitor;
    import listFW.*;

    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 listFW.visitor;
    import listFW.*;

    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 listFW.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.  This local inner class has access to the private field _fact of the outer object.  As result, besides the renaming of the parameters, the code of the anonymous (local) inner class is identical to that of the named helper visitor, HelpReverse.  Fill in the blanks!


    Reverse: builds the reverse of the IList host.

    Uses a named helper visitor called HelpReverse.

    Invert: builds the reverse of the IList host.

    Uses an anonymous local inner class helper visitor.

    
    package listFW.visitor;
    
    import listFW.*;
    
    /**
     * Reverses the host list by accumulating the 
     * reverse as one traverses the list.
     * This is best done via a helper visitor.  
     * The accumulated reverse list can be
     * viewed as a "stack": just stack the first 
     * as one traverses down the list; by the time 
     * the end is reached, the accumulated stack 
     * is the reverse list.
     * Remember that the end of a list is marked 
     * by the empty list.
     * An IListFactory is passed to the constructor.
     * @author D. X. Nguyen
     * @custom Copyright 2002 - All rights reserved
     */
    public class Reverse implements IListAlgo {
    
        private IListFactory _fact;
    
        public Reverse (IListFactory f) {
            _fact = f;
        }
    
        /**
        * Returns the host since the reverse of 
        * the empty list is the empty list.
        * @param host an IEmptyList
        * @param nu not used
        * @return host
        */
        public Object emptyCase(IEmptyList host, Object nu) {
            return host;
        }
    
        /**
        * Passes to the host's rest the list consisting 
        * of the host's first as the accumulated 
        * reverse of the list preceding the host's rest,
        * and asks for help to reverse the host.
        * @param host
        * @param nu nut used
        * @return INEList, the reverse of host.
        */
        public Object nonEmptyCase(INEList host, Object nu) {
            IList acc = _fact.makeNEList(host.getFirst(), 
                                      _fact.makeEmptyList());
            return  host.getRest().execute(new HelpReverse(_fact),
                                            acc);
        }
    }
    
    
    package listFW.visitor;
    
    import listFW.*;
    
    public class Invert implements IListAlgo {
    
        private IListFactory _fact;
    
        public Invert(IListFactory f) {
            _fact = f;
        }
    
        public Object emptyCase(IEmptyList host, Object nu) {
            return host;
        }
    
        public Object nonEmptyCase(INEList host, Object nu) {
            IList accRev = _fact.makeNEList(host.getFirst(),
                                      _fact.makeEmptyList());
            return host.getRest ().execute(new IListAlgo() {
    
                public Object emptyCase(IEmptyList h, Object acc) {
                    // TO DO
                }
    
                public Object nonEmptyCase(INEList h, Object acc) {
                    //TO DO
                }
            }  // end of anonymous local inner class
            , accRev);  // accumulated reverse passed to helper as input.
        }
    }
    
    
     
    
    package listFW.visitor;
    
    import listFW.*;
    
    /**
     * Reverses the host given the reverse of the list 
     * preceding the host.
     * Passes the accumulated reverse to the constructor.
     * @author D. X. Nguyen
     * @custom Copyright 2002 - All rights reserved
     */
    class HelpReverse implements IListAlgo {
        private IListFactory _fact;
    
        public HelpReverse(IListFactory f) {
            _fact = f;
        }
    
        /**
        * Returns the accumulated reverse because 
        * this is the end of the list.
        * @param host not used
        * @param acc an INEList, the accumulated reverse 
        * of the list preceding host.
        * @return acc
        */
        public Object emptyCase(IEmptyList host, Object acc) {
            return acc;
        }
    
        /**
        * Accumulates the reverse by cons-ing the 
        * host's first with the accumulated
        * reverse so far, and recur on the host's rest.
        * @param host the rest of the list to be reversed.
        * @param acc an INEList, the accumulated reverse 
        * of the list preceding host.
        * @return IList
        */
        public Object nonEmptyCase(INEList host, Object acc) {
            return host.getRest().execute (this,
                    _fact.makeNEList(host.getFirst(), (IList)acc));
        }
    }
    
    

     


  3. Assume an IList host contains distinct Integer objects.  Write a visitor called GetMin to find and return the minimum Integer object in the host.  Use anonymous inner classes as helpers.  

     

  4. Assume an IList host contains distinct Integer objects.  Write a visitor called RemMin to return a copy of the original list with its minimum Integer object removed.  Use anonymous inner classes as helpers.  

D. X. Nguyen, Sept. 15, 2002
dxnguyen@cs.rice.edu