2010-06-24 19 views
37

Ich sehe eine Menge Java-Code, wo Android Entwickler statische statische Klassen verwenden möchte. Besonders für Muster wie die ViewHolder Pattern in benutzerdefinierten ListAdapters.Warum bevorzugt Android statische Klassen?

Ich bin nicht sicher, was die Unterschiede zwischen statischen und nicht statischen Klassen sind. Ich habe darüber gelesen, aber es scheint keinen Sinn zu machen, wenn es um Leistung oder Speicherbedarf geht.

Antwort

62

Es ist nicht nur Android-Entwickler ...

Eine nicht-statische innere Klasse immer einen impliziten Verweis auf das einschließende Objekt hält. Wenn Sie diese Referenz nicht benötigen, ist alles nur Kostenspeicher. Bedenken Sie:

class Outer { 
    class NonStaticInner {} 
    static class StaticInner {} 
    public List<Object> foo(){ 
     return Arrays.asList(
      new NonStaticInner(), 
      new StaticInner()); 
    } 
} 

Wenn Sie es kompilieren, was Sie so etwas wie dieses erhalten wird:

class Outer { 
    Outer(){} 
    public List<Object> foo(){ 
     return Arrays.asList(
      new Outer$NonStaticInner(this), 
      new StaticInner()); 
    } 
} 
class Outer$NonStaticInner { 
    private final Outer this$0; 
    Outer$NonStaticInner(Outer enclosing) { this$0 = enclosing; } 
} 
class Outer$StaticInner { 
    Outer$StaticInner(){} 
} 
+2

Das stimmt, aber es gibt noch mehr - manchmal haben Sie keinen Bezug zur äußeren Klasse und wollen immer noch das Innere instanziieren (wenn es sichtbar ist). – ognian

+4

+1 für den Eindruck, was der Compiler aus der Eingabequelle macht – Seven

+0

Kann nicht eine nicht-statische innere Klasse explizit einen Verweis auf den äußeren deklarieren (muss nur manuell instantiiert werden) und gibt somit das Beste aus beiden Welten: die äußere Referenz ohne statische Speicherkosten? Ist eine statische innere Klasse nicht reentrant, weshalb sie nur dann nützlich ist, wenn eine einzelne Instanz benötigt wird? – samosaris

14

Statische innere Klassen (d. H. Klassen innerhalb einer anderen Klasse mit dem Schlüsselwort static deklariert) sind den "normalen" Klassen ziemlich ähnlich, außer dass Sie den Namensraum Ihres Pakets nicht verschmutzen. Das ist ihr (einziger) Unterschied und Nutzen und ich glaube, das ist der Grund, warum Sie es in Android sehen.

Verwenden Sie statische innere Klassen, wenn der Zweck der Klasse an die Hauptklasse angepasst wird, aber nicht von ihren Instanzen abhängt. Dies wird allgemein als eine gute Vorgehensweise angesehen.

+0

Können Sie näher auf die statische innere Klasse eingehen und was eine innere Klasse ausmacht? Das ist faszinierend – Mike

24

Der Hauptunterschied zwischen statischen und nicht statischen inneren Klassen besteht darin, dass eine nicht statische innere Klasse Zugriff auf andere Mitglieder der äußeren Klasse hat, selbst wenn sie privat sind. Nicht statische innere Klassen sind ein "Teil" der äußeren Klasse. Sie können weder erstellen noch können sie ohne eine Instanz einer äußeren Klasse existieren. Eine Konsequenz daraus ist, dass eine Instanz einer nicht statischen inneren Klasse zerstört wird, wenn die Instanz der äußeren Klasse zerstört wird.

Statische innere Klassen hingegen sind genau wie normale äußere Klassen. Die leben und sterben auf eigene Faust. Sie benötigen keine Instanz der äußeren Klasse für die innere Klasse. Das heißt, sie haben auch ihren eigenen Lebenszyklus. Sie werden zerstört, wenn der Müllsammler beschließt, sie zu zerstören.

Wie wirkt sich dies auf Speicher und/oder Leistung aus? Ich weiß es wirklich nicht. :)

+2

"Wie wirkt sich das auf Speicher und/oder Leistung aus?" Es verhindert Speicherverlust. Wenn Sie eine statische innere Klasse haben, können Sie sie nicht auf Aktivität/Fragment verweisen, da die statische innere Klasse nicht auf die äußere Elementvariable zugreifen kann. Wenn bei Activity ein Speicherverlust auftritt und diese Aktivität viele schwere Objekte (wie Ansichten) referenziert, wird unnötiger Speicher verbraucht und die Leistung verlangsamt. – aldok

4

Wenn Sie dekompilieren eine innere Klasse (oder schauen Sie es Debugger) können Sie sehen, dass es generierter Code für den Zugriff auf die Instanz der äußeren Klasse, mit der sie erstellt wurde. Der Overhead dafür ist mehr Speicher für den zusätzlichen Zeiger, mehr CPU für die Garbage-Collection wegen des zusätzlichen Pointers zum Testen, und wenn Sie nichts auswählen wollen, längere Kompilierzeit. Das Erstellen von Instanzen von nicht statischen inneren Klassen ist etwas komplizierter, da Sie eine Instanz der äußeren Klasse benötigen, um sie zu erstellen.

Die Sichtbarkeit von statischen und nicht statischen inneren Klassen kann gesteuert werden. Normalerweise sind sie privat, wenn ihre Implementierung stark mit internen Details der äußeren Klasse verbunden ist, und der Entwickler glaubt nicht, dass der Code wiederverwendet werden kann. In diesem Sinne sind sie nicht besser als private Funktionen. Innere Klassen können in Fällen wie Map.Entry öffentlich sein, wo die innere Klasse stark mit der von der Klasse offengelegten Schnittstelle verbunden ist, und der Entwickler denkt nicht, dass Map.Entry ohne irgendeine Art von Map verwendet werden kann. Beide Typen haben Zugriff auf private Mitglieder der äußeren Klasse und die äußere Klasse hat Zugriff auf private Mitglieder der inneren Klasse.

Instanzen von statischen und nicht statischen inneren Klassen werden wie jede andere Klasse gesammelt.Es gibt keine spezielle Verbindung zwischen der Grabage-Sammlung der äußeren Klasse und der Garbage Collection der inneren Klasse.

Im Fall von Implementierungen der UI-Klassen wie swing oder android sehen Sie statische innere Klassen, weil sie wie private Funktionen behandelt werden. Diese Klassen sind nicht für die Wiederverwendbarkeit außerhalb der äußeren Klasse entwickelt und sind stark mit der internen Implementierung der äußeren Klasse verbunden. Es gibt keinen Grund, sie zu entlarven und sicherzustellen, dass sie in mehr Fällen als dem spezifischen Kontext der äußeren Klassenanforderungen arbeiten können.

4

Eine nicht statische innere Klasseninstanz enthält einen Verweis auf die äußere Klasseninstanz, eine statische innere Klasseninstanz dagegen nicht.

Dies ist relevant für den Speicherbedarf der Anwendungen, da die versteckte Referenz zu Speicherverlusten führen kann - der Garbage Collector kann die äußere Klasseninstanz erst sammeln, wenn keine Referenzen mehr vorhanden sind. Auch die zusätzliche Referenz selbst benötigt Speicher, dies kann relevant sein, wenn eine große Anzahl von Instanzen verwendet wird.

class Outer{ 
    class Inner{//Only works with non static inner class 
      public Outer getOuter(){return Outer.this;} 
    } 
} 

Es ist für seine Verwendung auch von Bedeutung ist, ist der Verweis auf die äußeree Klasse ein Ctor Argument der inneren Klasse, eine neue nicht statische innere Klasse Objekt Sie den Ctor wie ein memberfunction auf einem verlangen zu erstellen Instanz der äußeren Klasse oder innerhalb einer Memberfunktion der äußeren Klasse. Dies bedeutet, dass Sie keine Instanz der inneren Klasse ohne eine Instanz der äußeren Klasse haben können.

Outer.Inner in = new Outer().new Inner();