Home | FAQ | Contact me

Interfaces in Java: the inner class confusion

—special thanks to my friend and Java guru, Scott Franson, for concepts and exact terminology.

Map.Entry


Perhaps, new to Java, you find yourself asking this question: "I understand interfaces, I understand Map and I've used interfaces and Map, HashMap, etc. for a while now. But the dot ends one token and precedes another. Therefore how to understand an interface that consists of two tokens Map and Entry, the one a class and the other an interface, separated by a dot?"

Answer

Take java.util.Map for the example. Map is the class. java.util. is the package, or namespace for the class Map.

Static, inner classes

It is the same with Map.Entry: the fully qualified name for the class Entry is: java.util.Map.Entry. The Entry class is different from the Map class in that it is defined as an inner class to Map and is not in its own .java file. It is usually accessed via Map.Entry because it is more common to have the java.util.Map in the import statements.

Map.Entry is an example of a public, static inner class. The public part is the normal accessor modifier. The inner class part means that the class definition sits within the definition of another block; in this case the Map class. There can be classes defined within methods and even normal statement blocks too. The static part means that you need not have an instance of Map to instantiate it. Meaning, if Entry were a concrete class and not an interface, you could instantiate it via:

Map.Entry myEntry = new Map.Entry();

If you included the following in your import statements:

import java.util.Map.Entry;

...you could instantiate it via:

Entry myEntry = new Entry();

Non-static inner classes

Non-static inner classes are a bit more tricky. Because they are non-static, you must have an instance of the outer class to instantiate it. So, if Entry were a non-static, public inner class of Map (and a concrete class), to get an instance of it would require:

Map myMap = new Map(); Map.Entry myEntry = myMap.new Entry();

In other words, to get an Entry you must have an instance of Map. Note the syntax of the new operator.

(However, Entry is most certainly NOT a non-static, inner class so do not become confused by the foregoing example.)

See this example for a static, non-static inner class.

public class InnerClassExample
{
	public static class PublicStaticInnerClass
	{
		public void doSomeStaticInnerClassThing()
		{
			System.out.println( "Static inner class" );
		}
	}

	public class PublicInnerClass
	{
		public void doSomeInnerClassThing()
		{
			System.out.println( "Non-static inner class" );
		}
	}

	public static void main( String[] args )
	{
		/* Reference the inner classes via the outer class to demonstrate
		 * the namespace.
		 */
		InnerClassExample.PublicStaticInnerClass publicStaticInnerClass
								= new InnerClassExample.PublicStaticInnerClass();

		publicStaticInnerClass.doSomeStaticInnerClassThing();

		/* Compile error: No enclosing instance of type InnerClassExample is accessible.
		 * Must qualify the allocation with an enclosing instance of type
		 * InnerClassExample (e.g. x.new A() where x is an instance of InnerClassExample).
		 *
		   InnerClassExample.PublicInnerClass publicInnerClass = new InnerClassExample.PublicInnerClass();
		 *
		 * Instead, do the following:
		 */

		InnerClassExample example                           = new InnerClassExample();
		InnerClassExample.PublicInnerClass publicInnerClass = example.new PublicInnerClass();

		publicInnerClass.doSomeInnerClassThing();
	}
}

Concrete example: Android code

Anonymous inner classes are yet different. Because they are anonymous, one cannot instantiate one apart from its definition. Meaning, the definition of an anonymous inner class IS the instantiation of it. Each time the code that defines the class is executed, an new instance is created. You cannot instantiate it otherwise; the code must be executed to create one.

This sort of thing goes on frequently in Android applications which use a lot of call-backs. The common example of:

confirmButton.setOnClickListener( new View.OnClickListener() { ... } );

demonstrates two of these concepts:

  1. a public static inner class (View.OnClickListener)
  2. an anonymous class definition (all the stuff inside the setOnClickListener() call).

Map.Entry   update: Java 5

Of course, in Java 5, you can't use Map.Entry without generics. The interface is now Map.Entry< K, V > and you must specify the type of the key, K, and its value, V. In the following example, we consume the arguments to main() to load a hashmap containing arguments and the number of times each occurs.

(Note: For some reason, you don't import java.util.Map.Entry since that results in a warning about never being used even though it is: "The import java.util.Map is never used.")

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class FunWithGenerics
{
	public static void main( String[] args )
	{
		HashMap< String, Integer >	hm = new HashMap< String, Integer >();

		hm.put( "Russ Bateman",  new Integer( 34 ) );
		hm.put( "Tom Buckley",   new Integer( 123 ));
		hm.put( "John Gay",      new Integer( 89 ) );
		hm.put( "Jeff Lawson",   new Integer( 99 ) );
		hm.put( "Thane Diamond", new Integer( 34 ) );

		// get a set of the entries...
		Set< Map.Entry<  String, Integer > >		set = hm.entrySet();

		// get an iterator for use in running the entries...
		Iterator< Map.Entry< String, Integer > >	i   = set.iterator();

		// display the entries...
		while( i.hasNext() )
		{
			Map.Entry< String, Integer > me = ( Map.Entry< String, Integer > ) i.next();
			System.out.print  ( me.getKey() + ": " );
			System.out.println( me.getValue() );
		}

		System.out.println();

		// add 1000 to Jeff Lawson...
		Integer	balance = ( ( Integer ) hm.get( "Jeff Lawson" ) ).intValue();
		hm.put( "Jeff Lawson", new Integer( balance + 1000 ) );
		System.out.println( "Jeff Lawson's new balance: " + hm.get( "Jeff Lawson" ) );
	}
}

The output from running the example above is:

Jeff Lawson: 99 Tom Buckley: 123 John Gay: 89 Thane Diamond: 34 Russ Bateman: 34 Jeff Lawson's new balance: 1099