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.
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#...
– Roger Barretto