A look at inner classes

Reduce class clutter in your Java designs: Use inner classes

1 2 Page 2
Page 2 of 2

In this twisted case, the value of k is modified before ee is returned. Now it would be nice to be able to defer the creation of the object instance until it was needed, but how would one know what value of k to use? The one when the new was executed or the one when the return was executed? The answer is you can't know, so the JavaSoft engineers decided that k has to be declared final so that compilers can enforce the fact that it will not change. That is exactly what the JavaSoft engineers did. When k is declared final, the statement changing its value will be rejected by the compiler, and the semantics of what k's value is becomes unambiguous.

And then there was this

When you're writing code in Java, inevitably there will be a time when you have to refer explicitly to an instance variable that is defined as an instance of the current object. You do that by using the this qualifier on a variable name. The name this.x refers to a variable named x in this instance of the object. It is pretty obvious that if you refer to this in an inner class, it refers to fields or variables defined in the inner class itself. However, we just got through a declaration that used an instance variable of the enclosing class -- rootNode -- and if we just had to define a variable in the inner class also named rootNode, how could we possibly tell them apart? The answer is that there is a new this in town: the qualified this.

A qualifier is now supported with regards to the this keyword. The definition of that qualifier is that you can use the name of any enclosing class as the qualifier for this. In our hypothetical example of an inner class defining its own field named rootNode, the value in the inner class is always this.rootNode, and the value in the enclosing class can be accessed with the name BinarySearchTree.this.rootNode. You can see that this name will always be unambiguous because Java 1.1 adds a rule that an inner class cannot have the same name as any of its enclosing classes.

There are a lot of ways to get confused about which class variable you are referring to, and I suggest you stick with unambiguous names for your instance variables. Fortunately, only the compiler writers have to know what you name an instance variable from an enclosing class that is itself an anonymous inner class.

Other ideas to consider

One issue that always pops up for new users of the Java language is the lack of pass-by-reference semantics for Java's method invocations. The response to queries on how to get pass-by-reference semantics is to create a class that points to a set-able value that you can later retrieve. The response to that answer, in turn, has always been a bit of disgust at having to create an entire class just so you could get back more than one value from a method invocation.

Consider the way in which inner classes address this issue in the bit of code shown below.

public class PointSample {
    public void samplePoint(IntSetter x, IntSetter y, FloatSetter m) {
    x.set(10);
    y.set(100);
    m.set(5.5f);
    }
}

The above code wants to return three return values: x, y, and m. So instead of passing local types, we pass in an object reference of type IntSetter or FloatSetter. The definition of IntSetter is as follows:

public interface IntSetter {
    public void set(int value);
}

As you can see, this simply is an interface that defines a single method, set, that takes an integer value and sets it. The design of FloatSetter is left as an exercise for the reader. Now consider the following example program:

public class example {
    int xPos = 0, yPos = 0;
    float   magnitude = 1.0f;
    public void doit() {
    PointSample ps = new PointSample();
    System.out.println("Shows off an anonymous inner class.");
    System.out.println("X,Y = ["+xPos+", "+yPos+"], Mag = "+magnitude);
    ps.samplePoint(new IntSetter() { public void set(int a) { xPos = a; }},
          new IntSetter() { public void set(int a) { yPos = a; }},
          new FloatSetter() { public void set(float a) { magnitude = a;}});
    System.out.println("X,Y = ["+xPos+", "+yPos+"], Mag = "+magnitude);
    }
    public static void main(String args[]) {
    example ex = new example();
    ex.doit();
    }
}

If you look carefully, all that the main method does is create an instance of an example object and invoke the doit method on it. The doit method is more interesting: It creates a PointSample object and invokes its samplePoint method with three inner classes.

The implementation of the inner class allows the implementation of the method samplePoint to simply store the results into instance variables of the calling class (in this case example). This technique eliminates the intermediate step of returning a class containing the values, and then removing the values from the class and storing them in the instance variables. What this example has accomplished is something like pass-by-reference, which means, in Java terms, that it passes a behavior that can modify the invoking object's state. Once you get the hang of it, you will see that the possibilities presented by inner classes are endless.

Wrapping up

Inner classes make the Java language more powerful in its ability to clearly express the intent of the programmer without adding a lot of clutter. Because they are implemented in the compiler, inner classes technically do not represent a change to Java (TM) the platform, but they are certainly a change to Java the language. Inner classes address a variety of complaints, and when combined with the Core Reflection API that we discussed last month, they enable people to build dynamically linked callback mechanisms. A good example of such a mechanism is the new event model in the AWT. There is a link to the inner class specification in the Resources section that I encourage you to read, even if you find it too dense for a quick read. Play with the examples and think about ways you might use these things to solve problems in the structure of your code.

Chuck McManis currently is the director of system software at FreeGate Corp., a venture-funded start-up that is exploring opportunities in the Internet marketplace. Before joining FreeGate, Chuck was a member of the Java Group. He joined the Java Group just after the formation of FirstPerson Inc. and was a member of the portable OS group (the group responsible for the OS portion of Java). Later, when FirstPerson was dissolved, he stayed with the group through the development of the alpha and beta versions of the Java platform. He created the first "all Java" home page on the Internet when he did the programming for the Java version of the Sun home page in May 1995. He also developed a cryptographic library for Java and versions of the Java class loader that could screen classes based on digital signatures. Before joining FirstPerson, Chuck worked in the operating systems area of SunSoft, developing networking applications, where he did the initial design of NIS+. Check out his home page.

Learn more about this topic

  • "Take an in-depth look at the Java Reflection API"
    Learn about the new Java 1.1 tools for finding out information about classes. http://www.javaworld.com/javaworld/jw-09-1997/jw-09-indepth.html
  • "Take a look inside Java classes"
    Learn to deduce properties of a Java class from inside a Java program. http://www.javaworld.com/javaworld/jw-08-1997/jw-08-indepth.html
  • "Build an interpreter in Java -- Implement the execution engine"
    Here's how to take the interpreter classes and run with them. http://www.javaworld.com/javaworld/jw-07-1997/jw-07-indepth.html
  • "How to build an interpreter in Java, Part 2The structure"
    The trick to assembling the foundation classes for a simple interpreter. http://www.javaworld.com/javaworld/jw-06-1997/jw-06-indepth.html
  • "How to build an interpreter in Java, Part 1The BASICs"
    For complex applications requiring a scripting language, Java can be used to implement the interpreter, adding scripting abilities to any Java app. http://www.javaworld.com/javaworld/jw-05-1997/jw-05-indepth.html
  • "Lexical analysis, Part 2Build an application"
    How to use the StreamTokenizer object to implement an interactive calculator. http://www.javaworld.com/javaworld/jw-02-1997/jw-02-indepth.html
  • "Lexical analysis and JavaPart 1"
    Learn how to convert human-readable text into machine-readable data using the StringTokenizer and StreamTokenizer classes. http://www.javaworld.com/javaworld/jw-01-1997/jw-01-indepth.html
  • "Code reuse and object-oriented systems"
    Use a helper class to enforce dynamic behavior. http://www.javaworld.com/javaworld/jw-12-1996/jw-12-indepth.html
  • "Container support for objects in Java 1.0.2"
    Organizing objects is easy when you put them into containers. This article walks you through the design and implementation of a container. http://www.javaworld.com/javaworld/jw-11-1996/jw-11-indepth.html
  • "The basics of Java class loaders"
    The fundamentals of this key component of the Java architecture. http://www.javaworld.com/javaworld/jw-10-1996/jw-10-indepth.html
  • "Not using garbage collection"
    Minimize heap thrashing in your Java programs. http://www.javaworld.com/javaworld/jw-09-1996/jw-09-indepth.html
  • "Threads and applets and visual controls"
    This final part of the series explores reading multiple data channels. http://www.javaworld.com/javaworld/jw-07-1996/jw-07-mcmanis.html
  • "Using communication channels in applets, Part 3"
    Develop Visual Basic-style techniques to applet design -- and convert temperatures in the process. http://www.javaworld.com/javaworld/jw-06-1996/jw-06-mcmanis.html
  • "Synchronizing threads in Java, Part II"
    Learn how to write a data channel class, and then create a simple example application that illustrates a real-world implementation of the class. http://www.javaworld.com/javaworld/jw-05-1996/jw-05-mcmanis.html
  • "Synchronizing threads in Java"
    Former Java team developer Chuck McManis walks you through a simple example illustrating how to synchronize threads to assure reliable and predictable applet behavior. http://www.javaworld.com/javaworld/jw-04-1996/jw-04-synch.html

Related:
1 2 Page 2
Page 2 of 2