CodeGym /Corsi /JAVA 25 SELF /Classi annidate statiche (static nested)

Classi annidate statiche (static nested)

JAVA 25 SELF
Livello 16, Lezione 1
Disponibile

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
static
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
outer.new Inner()
new Outer.Nested()
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.

1
Compito
JAVA 25 SELF, livello 16, lezione 1
Bloccato
Strumenti del mago: attivazione dell'incantesimo 🔮
Strumenti del mago: attivazione dell'incantesimo 🔮
1
Compito
JAVA 25 SELF, livello 16, lezione 1
Bloccato
Deposito segreto: livello di accesso 🛡️
Deposito segreto: livello di accesso 🛡️
1
Compito
JAVA 25 SELF, livello 16, lezione 1
Bloccato
Registro dei tesori: catena di artefatti 💎
Registro dei tesori: catena di artefatti 💎
1
Compito
JAVA 25 SELF, livello 16, lezione 1
Bloccato
Casa editrice: creazione di un nuovo libro 📚
Casa editrice: creazione di un nuovo libro 📚
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION