1. Classe annidata statica
Una classe annidata statica (static nested class) è una classe dichiarata all'interno di un'altra classe con il modificatore static. In sostanza è una classe normale che semplicemente “vive” dentro un'altra classe, ma non è legata al suo oggetto.
Se una inner class è come un fratello minore che tiene sempre la mano del maggiore (l'oggetto della classe esterna), allora la classe annidata statica è come un cugino che arriva solo alle feste di famiglia e per il resto vive la propria vita.
Differenze chiave:
- Non ha un riferimento implicito all'oggetto della classe esterna — niente OuterClass.this e nessun accesso ai membri non statici.
- Può contenere membri statici (a differenza di una inner class normale).
- Si istanzia senza un oggetto della classe esterna.
Sintassi di dichiarazione
Dichiarare una classe annidata statica è molto semplice: si usa la parola chiave static all'interno della classe esterna.
class Outer {
static class Nested {
void print() {
System.out.println("Hello from Nested!");
}
}
}
E questo è tutto! Niente costrutti sintattici astuti — semplicemente static class.
Visualizzazione:
Outer (classe esterna)
│
├── Nested (classe annidata statica)
│ └── print()
2. Creazione di un'istanza di una classe annidata statica
La parte più piacevole: non serve un oggetto della classe esterna!
Outer.Nested nested = new Outer.Nested();
nested.print(); // Hello from Nested!
Nota: usiamo il nome completo della classe — Outer.Nested. È come rivolgersi alla classe annidata tramite il cognome: “Smith.Son”.
Confronta con una inner class:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // serve un oggetto outer
Per una static nested class l'oggetto Outer non serve affatto!
3. Accesso ai membri della classe esterna
Qui si nasconde la principale differenza rispetto a una inner class.
- Una classe annidata statica ha accesso SOLO ai membri statici della classe esterna.
- Non può accedere a campi e metodi non statici (anche se sono public).
Esempio:
class Outer {
private static int staticValue = 10;
private int instanceValue = 20;
static class Nested {
void show() {
System.out.println("Static value: " + staticValue); // OK
// System.out.println("Instance value: " + instanceValue); // Errore!
}
}
}
Se provi ad accedere al campo non statico instanceValue, il compilatore ti farà subito una ramanzina.
Perché? Perché una static nested class non “sa” a quale oggetto di Outer collegarsi — non ha alcun riferimento all'oggetto della classe esterna.
4. Quando usare le classi annidate statiche
Quando sono appropriate?
- Quando la classe annidata è logicamente collegata a quella esterna, ma non richiede l'accesso all'oggetto della classe esterna.
- Quando vuoi incapsulare una struttura di supporto: per esempio, builder, un'utility, un enum o anche un piccolo oggetto immutabile.
- Quando vuoi ridurre il “rumore” del package: la classe serve solo a quella esterna, non ha senso estrarla.
Casi tipici:
- Pattern Builder (soprattutto per oggetti immutabili)
- Implementazione di strutture di supporto: ad esempio classi Node interne nelle collezioni
- Raggruppamento di costanti o utility
Regola semplice per la scelta
Fatti una domanda: “La mia classe annidata ha bisogno di accedere a un oggetto specifico della classe esterna?”
NO → usa static class (classe annidata statica)
SÌ → usa una normale class (inner)
5. Esempi d'uso
Esempio 1: Builder per una classe
Supponiamo di avere la classe Person e di voler implementare per essa il pattern Builder:
public class Person {
private final String name;
private final int age;
// Costruttore privato
private Person(Builder builder) {
this.name = builder.name;
this.age = builder.age;
}
// Classe annidata statica Builder
public static class Builder {
private String name;
private int age;
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public Person build() {
return new Person(this);
}
}
public void printInfo() {
System.out.println("Person: " + name + ", " + age);
}
}
Uso:
Person person = new Person.Builder()
.setName("Ivan")
.setAge(30)
.build();
person.printInfo(); // Person: Ivan, 30
Perché Builder è una classe annidata statica?
Perché non dipende da un oggetto Person, ma aiuta solo a crearlo. È logicamente collegato a Person, ma non a una specifica istanza.
Esempio 2: Struttura di supporto all'interno di una collezione
Immaginiamo una classe “scatola di interi”, in cui per memorizzare gli elementi si usa una classe interna statica Node:
public class IntBox {
private Node head;
// Static nested class
private static class Node {
int value;
Node next;
Node(int value) {
this.value = value;
}
}
public void add(int value) {
Node node = new Node(value);
node.next = head;
head = node;
}
public void printAll() {
Node current = head;
while (current != null) {
System.out.println(current.value);
current = current.next;
}
}
}
Uso:
IntBox box = new IntBox();
box.add(1);
box.add(2);
box.add(3);
box.printAll(); // 3 2 1
Perché Node è static?
Perché ogni Node non deve conoscere l'intera scatola (IntBox): memorizza solo i dati e il riferimento al Node successivo.
Esempio 3: Classe di utilità all'interno della principale
public class MathUtils {
// Classe annidata statica per lavorare con i numeri complessi
public static class Complex {
private final double re;
private final double im;
public Complex(double re, double im) {
this.re = re;
this.im = im;
}
public Complex add(Complex other) {
return new Complex(this.re + other.re, this.im + other.im);
}
@Override
public String toString() {
return re + " + " + im + "i";
}
}
}
Uso:
MathUtils.Complex a = new MathUtils.Complex(1, 2);
MathUtils.Complex b = new MathUtils.Complex(3, 4);
MathUtils.Complex sum = a.add(b);
System.out.println(sum); // 4.0 + 6.0i
6. Dettagli utili
Inner class vs classe annidata statica
| Classe interna (inner) | Annidata statica (static nested) | |
|---|---|---|
| Parola chiave | no | |
| Riferimento implicito all'oggetto esterno | sì | no |
| Accesso ai membri non statici della classe esterna | sì | no |
| Accesso ai membri statici della classe esterna | sì | sì |
| Può contenere membri statici | no (solo costanti) | sì |
| Sintassi di creazione | |
|
| Uso | Quando serve l'accesso all'oggetto della classe esterna | Quando non serve l'accesso all'oggetto della classe esterna |
Illustrazione
flowchart LR
OuterClass -->|has| InnerClass
OuterClass -.->|has| StaticNestedClass
StaticNestedClass -.->|can access| staticMembers
InnerClass -->|can access| instanceMembers
InnerClass -->|can access| staticMembers
Caratteristiche e limitazioni
- Static nested class può contenere sia campi e metodi normali sia statici.
- Può essere dichiarata con qualsiasi modificatore di accesso (public, private, protected, package-private).
- Può implementare interfacce ed estendere altre classi.
- Può essere generic.
- Di solito si usa per incapsulare classi di servizio che non servono al di fuori della classe esterna.
Esempio di generic static nested class:
public class Box {
public static class Holder<T> {
private T value;
public Holder(T value) { this.value = value; }
public T get() { return value; }
}
}
Quando NON conviene usare una static nested class
- Se la classe annidata ha bisogno di accedere a campi/metodi non statici della classe esterna — usa una normale inner class.
- Se la classe serve anche al di fuori della classe esterna — spostatela in un file separato.
- Se la classe è troppo grande o complessa — meglio renderla separata.
7. Errori tipici e dettagli
Errore n. 1: confusione tra inner e static nested class.
Molti principianti tentano, in una static nested class, di accedere ai campi non statici della classe esterna. Ma una static nested class non ha un riferimento all'oggetto della classe esterna, quindi non è possibile. Se ti serve accedere allo stato di un oggetto specifico — usa una normale inner class.
Errore n. 2: tentativo di creare una static nested class tramite un oggetto della classe esterna.
Non c'è bisogno di scrivere outer.new Inner(). Per una static nested class usa sempre new Outer.Nested().
Errore n. 3: uso di una static nested class per logica che richiede l'accesso all'istanza della classe esterna.
Se la logica della classe è strettamente legata allo stato dell'oggetto esterno, una static nested class è una scelta sbagliata. Usa una normale classe interna.
Errore n. 4: annidamento eccessivamente complesso.
Non abusare delle classi annidate. Se la struttura diventa confusa, è meglio estrarre alcune classi.
GO TO FULL VERSION