Why can an inner class be Static and an outer class not?

Asked

Viewed 11,031 times

20

It is possible to have an internal Static class:

public class ClasseExterna{
    public static class ClasseInterna(){
    }
}

And it is not possible to have an external class Static:

public static class ClasseExterna{
    public static class ClasseExterna(){
    }
}

What is the difference between one situation and another?

  • Honestly, never use an inner class. It’s an aberration to object orientation. I didn’t even know Java let me do it, I thought it was just C#...

2 answers

14


static means that something belongs directly to the class and does not need an instance of that class in order to access it.

What can be static:

  • Methods;
  • attributes; and
  • Nested classes.

Static Nested Class x Non-static Nested Class

Comparing two classes, one static and the other not, but both nested, we will realize what the definition of static means, which I quoted above:

class Externa {
    static class Estatica {
        void imprime() {
            System.out.println("método da classe aninhada estática");
        }
    }
    class NaoEstatica {
        void imprime() {
            System.out.println("método da classe aninhada NÃO estática");
        }   
    }
}

class TesteNestedClasses {
    public static void main(String[] args) {
        /*
         * repare que para criar uma instancia da classe estatica não foi necessário
         * criar uma instância da classe Externa
         */
        Externa.Estatica estatica = new Externa.Estatica();
        estatica.imprime();

        /*
         * não é possível criar uma classe não estática sem uma instancia da classe Externa
         */
        //Externa.NaoEstatica naoEstatica = new Externa.NaoEstatica(); //ERRADO!!!
        Externa externa = new Externa();
        Externa.NaoEstatica naoEstatica = externa.new NaoEstatica(); 
        naoEstatica.imprime();
    }
}

Upshot:

static nested class method
NON-STATIC nested class method

We cannot have an external class (i.e., not nested) of the static type because according to the definition, static is that which can be accessed directly by the class, without the need for an instance, so if the class is not nested to whom it would belong if it were static? It doesn’t make sense something like that, so there’s no way to implement something like that.

7

Only classes declared within other classes (known as member classes) may be declared static. Classes top-level, anonymous or local (i.e., declared within methods) cannot.

Makes sense: in Java, the modifier static is used to modify the relationship of some entity (class, method, attribute) with the class that owns it. Generally the modifier means that the entity belongs to, or has access to, the class to which it belongs but not to instances of this class. If a class is top-level, she is not possessed by any other: how could it be static, then?

Now the crucial question is: what the modifier static makes in member classes?

Two definitions

The difference between a static member class and a nonstatic member class is subtle. If I were to define it in a very abstract expression, I would say that

  • Every instance of nonstatic member classes is associated with an external class instance.
  • Instances of static member classes are not associated to an external class instance.

The practical conclusion of this is that instances of non-static classes can directly access the attributes and methods (both static and non-static) of the instance to which it is associated. Instances of static classes cannot access attributes and non-static methods of any external class instance - only static attributes of the external class are directly accessible to you.

Okay, this is very abstract. Let’s concretize the concept.

What we can only do with non-static classes

Non-static classes are always attached to an instance of the outer class. Therefore, we can retrieve values from this instance. Consider the example class below, where we have both a static and a non-static class:

public class OuterClass {

    public static class StaticClass {}

    public class NonStaticClass {}

    private int field;

    public OuterClass(int field) {
        this.field = field;
    }

    public static void main(String[] args) {
        OuterClass obj1 = new OuterClass(1);
        OuterClass obj2 = new OuterClass(2);
    }
}

Okay, now I’m gonna add to NonStaticClass a method that returns the value of field of the instance of OuterClass. If it were to take the instance of NonStaticClass, we would use this. To take the outer instance, we put OuterClass.this:

    public class NonStaticClass {
            public int getOuterField() {
                    return OuterClass.this.field;
            }
    }

Now, let’s print, on the OuterClass, the value returned by NonStaticClass.getOuterField():

    public OuterClass(int field) {
            this.field = field;
            System.out.println(new NonStaticClass().getOuterField());
    }

    public static void main(String[] args) {
            OuterClass obj1 = new OuterClass(1);
            OuterClass obj2 = new OuterClass(2);
    }
}

If I run this class, the result will be:

1
2

Note that the method NonStaticClass.getOuterField() returns the value of the instance field in which it was called. This is only possible because the instance of NonStaticClass knows the instance of OuterClass.

What we can’t do with non-static classes

One consequence of this is that I cannot use non-static classes within static methods. For example, I cannot do this:

   public static void main(String[] args) {
        OuterClass obj1 = new OuterClass(1);
        OuterClass obj2 = new OuterClass(2);

        // A linha abaixo não compila
        // NonStaticClass nonStaticClassInstance = new NonStaticClass();
    }

Why not? Because every instance of NonStaticClass must be linked to an instance of OuterClass, but the method main(String[]) is static, is not "connected" to any instance of OuterClass. It’s the same reason we can’t use this within a static method. However, we can create a new instance of NonStaticClass in a static method (or even another class) from an instance of OuterClass:

   public static void main(String[] args) {
        OuterClass obj1 = new OuterClass(1);
        OuterClass obj2 = new OuterClass(2);

        // A linha abaixo compila
        NonStaticClass instance = obj2.new NonStaticClass();
        // Imprimirá "2"
        System.out.println(instance.getOuterField());
   }

Another consequence of using a nonstatic class is that it cannot have static fields or methods. I confess that I do not understand well the reason for this, but it is certainly not something that is needed.

What we can only do with static classes

Static classes in turn, nay need an external class instance. One consequence of this is that they can be used in static methods without enclosing instances:

public static void main(String[] args) {
    // A linha abaixo compila
    StaticClass instance2 = new StaticClass();
}

In fact, if they are public, the static classes can be used even by other classes, in a very similar way to the classes top-level. Usually, by the way, they’re used like this:

public class Main {
    public static void main(String[] args) {
        OuterClass.StaticClass obj1 = new OuterClass.StaticClass();

        // Classes não-estáticas públicas também podem ser usadas, mas
        // sempre atreladas a uma instância da classe externa.
        OuterClass outerClass = new OuterClass(3);
        OuterClass.NonSTaticClass obj2 = outerClass.new NonStaticClass();
    }
}

What we can’t do with static classes

Since instances of static classes are not linked to instances of the external class, they cannot access fields and methods of these instances:

private static class StaticClass {
    // O método abaixo não compila
    // public int getOuterField() {
    //     return field;
    // }
}

On the other hand, both static and nonstatic classes can access fields and methods static of the external class, since these fields and methods are linked to a specific instance:

private int field;
private static int staticField = 0;

private static class StaticClass {
    public int getStaticOuterField() {
        return staticField;
    }
    // O método abaixo não compila
    // public int getOuterField() {
    //      return field;
    // }
}

private class NonStaticClass {
    // O método abaixo compila
    public int getStaticOuterField() {
        return staticField;
    }
    // O método abaixo compila
    public int getOuterField() {
        return field;
    }
}

In practice, this makes the static classes practically equivalent to classes top-level.

What are static classes for, then?

The main reason to use static classes is code structuring.

Suppose you have a class Connection as the below:

public class Connection {
    public List getResult() throws ConnectionException {
        // Faz algo
        return null;
    }
}

She needs a specific exception for herself. This exception could be created in another package, but many developers like to declare it within the class itself, to maintain cohesion:

public class Connection {
    public static class ConnectionException extends Exception {}    

    public List getResult() throws ConnectionException {
            // Faz algo
            return null;
    }
}

It may seem a little complicated, but there are advantages:

  • The new class will have access to private attributes and methods of the external class, reducing the need for getters and setters.
  • It encourages modularization: a piece of an algorithm could be extracted into a class, but creating another file would complicate its code. If you can create this class within the other, it already simplifies.
  • Cohesion help: the extracted class will be "pasted" in the most relevant code, as well as the above exception.
  • Since internal classes are generally not relevant to other parts of the program, creating them as private classes favors encapsulation

What are non-static classes for?

In my experience, non-static classes are less common. Even when they exist, they could be replaced by static classes. Still, they can be useful for creating objects that are closely linked to the external instance.

A good example might be iterators. Consider the class below, which allows you to use arrays as if they were lists:

public class ArrayEnclosingList<T> extends AbstractList<T> {

    private T array[]; 

    public ArrayEnclosingList(T[] array) {
        this.array = array;
    }

    public T get(int index) {
        return array[index];
    }

    public int size() {
        return array.length;
    }
}

AbstractList already provides a method iterator(), but we might want to overwrite it (often to optimize it). In our case, we could do something like:

public class ArrayEnclosingList<T> extends AbstractList<T> {

    private T array[]; 

    // ...

    private class ArrayEnclosingListIterator implements Iterator<T> {

        private int pointer = 0;

        public boolean hasNext() {
            return pointer < array.length;
        }

        public T next() {
            return array[pointer++];
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

    }

    public Iterator<T> iterator() {
        return new ArrayEnclosingListIterator();
    }
}

Note how the methods of ArrayEnclosingListIterator can access array. This is only possible because ArrayEnclosingListIterator is a non-static member class. Could I do that with a class that is not internal? Certainly, but it would complicate somewhat:

public class ArrayEnclosingListIterator<T> implements Iterator<T> {

    private int pointer = 0;
    // Um atributo a mais
    private ArrayEnclosingList<T> list;

    // É preciso passar a instância da lista na mão
    public ArrayEnclosingListIterator(ArrayEnclosingList<T> list) {
        this.list = list;
    }

    public boolean hasNext() {
        // Agora o array da lista deve ser acessível
        return pointer < list.getArray().length; 
    }

    public T next() {
        // Agora o array da lista deve ser acessível
        return list.getArray()[pointer++];
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }
}

In this case, as the iterator is closely linked to the eternal, the advantage of putting it as a member class is evident.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.