Classe nidificata statica in Java, perché?


217

Stavo cercando il codice Java LinkedListe ho notato che faceva uso di una classe nidificata statica Entry.

public class LinkedList<E> ... {
...

 private static class Entry<E> { ... }

}

Qual è la ragione per usare una classe annidata statica, piuttosto che una normale classe interna?

L'unico motivo che mi viene in mente è che Entry non ha accesso alle variabili di istanza, quindi dal punto di vista OOP ha un migliore incapsulamento.

Ma ho pensato che potrebbero esserci altri motivi, forse la performance. Cosa potrebbe essere?

Nota. Spero di aver corretto i miei termini, l'avrei definita una classe interna statica, ma penso che sia sbagliato: http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html


Risposte:


271

La pagina Sun a cui ti colleghi presenta alcune differenze chiave tra i due:

Una classe nidificata è un membro della sua classe che la racchiude. Le classi nidificate non statiche (classi interne) hanno accesso ad altri membri della classe che la racchiude, anche se sono dichiarate private. Le classi nidificate statiche non hanno accesso ad altri membri della classe che la racchiude.
...

Nota: una classe nidificata statica interagisce con i membri dell'istanza della sua classe esterna (e altre classi) proprio come qualsiasi altra classe di livello superiore. In effetti, una classe nidificata statica è comportamentalmente una classe di livello superiore che è stata nidificata in un'altra classe di livello superiore per comodità di packaging.

Non è necessario LinkedList.Entryessere una classe di livello superiore poiché viene utilizzata solo da LinkedList(ci sono altre interfacce che hanno anche classi nidificate statiche denominate Entry, come Map.Entry- stesso concetto). E poiché non ha bisogno di accedere ai membri di LinkedList, ha senso che sia statico: è un approccio molto più pulito.

Come sottolinea Jon Skeet , penso che sia una buona idea se si utilizza una classe nidificata è iniziare con essa statica, quindi decidere se deve realmente essere non statica in base al proprio utilizzo.


Bah, non riesco a ottenere un link di ancoraggio al commento per funzionare, ma è questo commento:#comment113712_253507
Zach Lysobey,

1
@matt b Se una classe nidificata statica non ha accesso ai membri dell'istanza della classe Outer, come interagisce con i membri dell'istanza della classe Outer?
Geek,

1
@mattb Ma come ha notato @Geek, la pagina Sun è contraddittoria: A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class come è possibile se solo un paragrafo prima che i documenti dicano: Static nested classes do not have access to other members of the enclosing class Forse vorrebbero dire: A nested (non-static) class interacts with the instance members of its outer class (and other classes) just like any other top-level class
tonix

1
@DavidS Grazie per il link! Sì, ho sbagliato, leggendo il mio commento ora vedo che la mia riformulazione non è corretta. Come detto: An inner class interacts with the instance members through an implicit reference to its enclosing classe questo fa notare un'altra proprietà interessante non-static inner classesnonché anonymous inner classeso local classes defined inside a block: tutti non possono avere un no-argcostruttore causa il compilatore implicitamente anteporre la sequenza arg di ogni costruttore per passare un riferimento di un'istanza del racchiude classe. Abbastanza semplice.
tonix,

1
puoi usare la classe interna statica per creare un'istanza della classe esterna che ha solo un costruttore privato. Questo è usato nel modello del builder. Non puoi fare lo stesso con la classe interna.
seenimurugan,

47

A mio avviso, la domanda dovrebbe essere al contrario ogni volta che vedi una classe interna - deve davvero essere una classe interna, con la complessità aggiuntiva e il riferimento implicito (piuttosto che esplicito e più chiaro, IMO) a un'istanza della classe contenente?

Intendiamoci, sono di parte come fan di C # - C # non ha l'equivalente di classi interne, sebbene abbia tipi nidificati. Non posso dire di aver perso ancora le lezioni interne :)


4
Potrei sbagliarmi, ma mi sembra un esempio di una classe annidata statica, non di una classe interna. Nell'esempio specificano persino che non hanno accesso alle variabili di istanza sulla classe circostante nella classe nidificata.
ColinD

Sì, Colin ha ragione: C # non ha classi interne, ha classi nidificate. Intendiamoci, una classe nidificata statica in C # non è la stessa di una classe nidificata statica in Java :)
Jon Skeet,

2
I tipi nidificati sono una di quelle aree in cui C # lo ha ottenuto in modo estremamente corretto rispetto a Java. Mi stupisco sempre della sua correttezza semantica / logica.
nawfal,

3
@nawfal: Sì, a parte qualche negligo, sono meravigliato di quanto bene sia stato progettato (e specificato) il linguaggio C #.
Jon Skeet,

1
@JonSkeet hai un articolo o un blog su cosa sono quei negri? Mi piacerebbe passare attraverso quello che trovi come "negri" :)
nawfal

27

Ci sono problemi di conservazione della memoria non ovvi da prendere in considerazione qui. Poiché una classe interna non statica mantiene un riferimento implicito alla sua classe "esterna", se un'istanza della classe interna è fortemente referenziata, anche l'istanza esterna è fortemente referenziata. Questo può portare a qualche grattacapo quando la classe esterna non viene raccolta in modo inutile, anche se sembra che nulla vi faccia riferimento.


Se la classe 'esterna' è definitiva e quindi non può essere istanziata affatto, questo argomento ha senso in quel caso? Perché avere / mantenere un riferimento a una classe esterna è inutile, se quest'ultima è definitiva.
getadzeg,

10

Bene, per prima cosa, le classi interne non statiche hanno un campo nascosto aggiuntivo che punta all'istanza della classe esterna. Quindi se la classe Entry non fosse statica, quindi oltre ad avere accesso di cui non ha bisogno, porterebbe circa quattro puntatori anziché tre.

Di norma, direi che se definisci una classe che è praticamente lì per fungere da raccolta di membri di dati, come una "struttura" in C, considera di renderla statica.


8

La classe interna statica viene utilizzata nel modello del costruttore. La classe interna statica può creare un'istanza della sua classe esterna che ha solo un costruttore privato. Quindi puoi usare la classe interna statica per creare un'istanza della classe esterna che ha solo un costruttore privato. Non puoi fare lo stesso con la classe interna in quanto devi creare un oggetto della classe esterna prima di accedere alla classe interna.

class OuterClass {
    private OuterClass(int x) {
        System.out.println("x: " + x);
    }

    static class InnerClass {
        public static void test() {
            OuterClass outer = new OuterClass(1);
        }
    }
}

public class Test {
    public static void main(String[] args) {
        OuterClass.InnerClass.test();
        // OuterClass outer = new OuterClass(1); // It is not possible to create outer instance from outside.
    }
}

Questo produrrà x: 1


7

la classe nidificata statica è come qualsiasi altra classe esterna, in quanto non ha accesso ai membri della classe esterna.

Solo per comodità di confezionamento, possiamo raggruppare le classi nidificate statiche in una classe esterna ai fini della leggibilità. A parte questo, non esiste altro caso d'uso di classe nidificata statica.

Esempio per questo tipo di utilizzo, puoi trovare nel file Android R.java (risorse). La cartella Res di Android contiene layout (contenenti disegni dello schermo), cartella disegnabile (contenente immagini utilizzate per il progetto), cartella valori (che contiene costanti di stringa), ecc.

Sine tutte le cartelle fanno parte della cartella Res, lo strumento Android genera un file R.java (risorse) che contiene internamente molte classi nidificate statiche per ciascuna delle loro cartelle interne.

Ecco l'aspetto del file R.java generato in Android: qui vengono utilizzati solo per comodità di packaging.

/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found.  It
 * should not be modified by hand.
 */

package com.techpalle.b17_testthird;

public final class R {
    public static final class drawable {
        public static final int ic_launcher=0x7f020000;
    }
    public static final class layout {
        public static final int activity_main=0x7f030000;
    }
    public static final class menu {
        public static final int main=0x7f070000;
    }
    public static final class string {
        public static final int action_settings=0x7f050001;
        public static final int app_name=0x7f050000;
        public static final int hello_world=0x7f050002;
    }
}


4

Esempio semplice:

package test;

public class UpperClass {
public static class StaticInnerClass {}

public class InnerClass {}

public static void main(String[] args) {
    // works
    StaticInnerClass stat = new StaticInnerClass();
    // doesn't compile
    InnerClass inner = new InnerClass();
}
}

Se non statico, la classe non può essere istanziata se non in un'istanza della classe superiore (quindi non nell'esempio in cui main è una funzione statica)


il tuo StaticInnerClass non è, in effetti, una classe nidificata / interna statica. è una classe statica di livello superiore.
theRiley

2

Uno dei motivi per statico vs. normale ha a che fare con il caricamento di classe. Non è possibile creare un'istanza di una classe interna nel costruttore del relativo genitore.

PS: ho sempre capito che "nidificato" e "interno" sono intercambiabili. Potrebbero esserci sottili sfumature nei termini, ma la maggior parte degli sviluppatori Java lo capirebbe.


1

Le classi interne non statiche possono causare perdite di memoria mentre le classi interne statiche proteggono da esse. Se la classe esterna contiene dati considerevoli, può ridurre le prestazioni dell'applicazione.


"statico interno" è una contraddizione in termini.
Marchese di Lorne,

1
@EJP, sheesh ... le persone escono davvero sottolineandolo ogni volta che qualcuno menziona "classi interne statiche" ...
Sakiboy

0

Non conosco la differenza di prestazioni, ma come dici tu, la classe nidificata statica non fa parte di un'istanza della classe che la racchiude. Sembra solo più semplice creare una classe nidificata statica a meno che non sia davvero necessario che sia una classe interna.

È un po 'come il motivo per cui ho sempre reso definitive le mie variabili in Java - se non sono definitive, so che c'è qualcosa di divertente in esse. Se si utilizza una classe interna anziché una classe nidificata statica, dovrebbe esserci una buona ragione.


Una classe più interna non fa neanche parte di un'istanza della classe che la racchiude.
Marchese di Lorne,

una classe interna dipende esistenzialmente dalla classe che la racchiude e ha un accesso intimo ai membri della classe di clausura, quindi in realtà fa parte della classe di clausura. infatti, è un membro.
theRiley

0

L'uso di una classe nidificata statica anziché non statica può in alcuni casi risparmiare spazio. Ad esempio: implementare Comparatoruna classe interna, ad esempio Studente.

public class Student {
  public static final Comparator<Student> BY_NAME = new ByName();
  private final String name;
  ...
  private static class ByName implements Comparator<Student> {
    public int compare() {...}
  }
}

Quindi staticassicura che la classe Studente abbia un solo comparatore, anziché creare un'istanza nuova ogni volta che viene creata una nuova istanza di studente.


-1

Adavantage of inner class--

  1. uso una volta
  2. supporta e migliora l'incapsulamento
  3. readibility
  4. accesso al campo privato

Senza esistere della classe esterna, la classe interna non esisterà.

class car{
    class wheel{

    }
}

Esistono quattro tipi di classe interna.

  1. classe interna normale
  2. Metodo Classe interna locale
  3. Classe interna anonima
  4. classe interna statica

punto ---

  1. dalla classe interna statica, possiamo accedere solo al membro statico della classe esterna.
  2. All'interno della classe interna non possiamo dichiarare membro statico.
  3. inorder per invocare la normale classe interna nell'area statica della classe esterna.

    Outer 0=new Outer(); Outer.Inner i= O.new Inner();

  4. inorder per invocare la normale classe interna nell'area di istanza della classe esterna.

    Inner i=new Inner();

  5. al fine di invocare la normale classe interna al di fuori della classe esterna.

    Outer 0=new Outer(); Outer.Inner i= O.new Inner();

  6. inside Classe interna Questo puntatore alla classe interna.

    this.member-current inner class outerclassname.this--outer class

  7. per il modificatore applicabile della classe interna è - pubblico, predefinito,

    final,abstract,strictfp,+private,protected,static

  8. outer $ inner è il nome del nome della classe interna.

  9. classe interna all'interno del metodo di istanza quindi possiamo accedere al campo statico e di istanza della classe esterna.

10. classe interna all'interno del metodo statico, quindi possiamo accedere solo al campo statico di

classe esterna.

class outer{

    int x=10;
    static int y-20;

    public void m1() {
        int i=30;
        final j=40;

        class inner{

            public void m2() {
                // have accees x,y and j
            }
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.