What's all the static, man?

The difference between inner member classes and inner static member classes

In this Q&A I'll cover two popular and related questions: "What is the difference between an inner member class and an inner static member class?" And: "Why would I choose one over the other?"

Superficially, static and nonstatic inner member classes differ in how you declare them. A static member class will have the static keyword in its definition, while a member class will not:

public class InnerClassSyntax {
    // A static member class
    public static class StaticMember {
        // ... code
    }
    
    // A member class
    public class Member {
        // ... code
    }
}

More importantly, the static keyword limits what instances of the static member classes can do to instances of the class within which they are defined. To wit, when you declare a member class as static, instances of that inner class will have access only to the static methods and static members of the enclosing instance.

In contrast, plain old member classes can access any method or member of the enclosing class since member classes have access to the enclosing instance's this. (A static member class is limited to accessing only static attributes because it lacks access to the enclosing instance's this.)

Let's expand the original example to see these limitations in action:

public class InnerClassSyntax {
    private static int _aStaticInstanceVariable;
    private        int _anInstanceVariable;
    
    public void anInstanceMethod() {
        // ... do something
    }
    
    public static void aStaticMethod() {
        // ... do something
    }
    
    // A static member class
    public static class StaticMember {
        public void aMethod() {
            // Legal calls
            int staticValue = InnerClassSyntax._aStaticInstanceVariable;
            InnerClassSyntax.aStaticMethod();
            
            // Illegal calls -- will not compile if uncommented
            // int value = _anInstanceVariable;
            // anInstanceMethod();
            // InnerClassSyntax.anInstanceMethod();
        }
    }
    
    // A member class
    public class Member {
        public void aMethod() {
            // Legal calls
            int staticValue = _aStaticInstanceVariable;
            int value       = _anInstanceVariable;
            aStaticMethod();
            anInstanceMethod();
        }
    }
    
}

You see that the static member class can access only those attributes declared as static. Static member classes do not have access to the this reference -- in this case InnerClassSyntax.this. However, Member instances do have access to InnerClassSyntax.this, so they can access everything in the enclosing InnerClassSyntax instance.

The following code selection rewrites the Member class so that it explicitly uses the InnerClassSyntax.this reference (which normally happens by default):

    public class Member {
        public void aMethod() {
            staticValue = InnerClassSyntax._aStaticInstanceVariable;
            value = InnerClassSyntax.this._anInstanceVariable;
            InnerClassSyntax.aStaticMethod();
            InnerClassSyntax.this.anInstanceMethod();
        }

Now, with all the syntactical goodies aside, why would you choose a static member class over a plain-old member class? Well, when designing an inner class, you need to ask what do that inner class's instances need? Ask yourself whether the instances depend on instance-specific information? If yes, you need a member class.

However, nonstatic member classes must maintain a reference to the enclosing instance. Maintenance of this reference consumes both memory and CPU time. Therefore, if your member class does not depend upon the enclosing class's instance data, you can, and should, declare it static.

Let's look at a static member class example:

public final class Text {
    // List of valid justification constants
    public static final Justification LEFT =
        new Justification( "Left" );
    
    public static final Justification RIGHT =
        new Justification( "Right" );
    
    public static final Justification CENTER =
        new Justification( "Center" );
    
    // Holder for this instance's justification
    private Justification _justification;
    
    public void setJustification( Justification justification ) {
        _justification = justification;
    }
    
    // Other Text code omitted
    // . . . 
    
    public static class Justification {
        
        private String _label;
        
        public Justification( String label ) {
            _label = label;
        }
        
        public String toString() {
            return _label;
        }
        
    }
    
}

In the code above, instances of Text might represent selections of text inside an editor. The text can have a justification. I declared the Justification member class as static since the Justification instances do not rely on instance-level information in the enclosing Text class. Instead, instances of Justification act as constants that work with any instance of Text. That's why we can get away with declaring Justification static; it is independent of any specific Text instances.

Now, let's look at a member class example:

public final class Text {
    private String _text;
    private Search _search = new Search();
    
    public boolean contains( String text ) {
        return _search.contains( text );
    }
    
    private class Search {
        public boolean contains( String text ) {
            // Do something to Text._text;
            // Hardcode return so that this class can compile
            return false;
        }
        // ... other methods
    }
    
    // Original justification code removed for brevity
    
}

In this example, Text implements its searching code as a member class. (Such an approach nicely partitions the code, rather than embedding the search code within the Text class itself.) In order to search, the Search object needs access to Text.this._text, thus the Search must not be declared static.

Tony Sintes is a senior principal consultant at BroadVision. Tony, a Sun-certified Java 1.1 programmer and Java 2 developer, has worked with Java since 1997.

Learn more about this topic