2009-02-13 9 views
5

Ich habe kürzlich ein neues Projekt gestartet und versuche, meine Instanzvariablen immer auf einen Wert zu initialisieren, so dass keiner von ihnen zu irgendeinem Zeitpunkt null ist. Kleines Beispiel unten:Sollte ich Instanzvariablen in Java immer initialisiert halten oder nicht?

public class ItemManager { 

    ItemMaster itemMaster; 
    List<ItemComponentManager> components; 

    ItemManager() { 
    itemMaster = new ItemMaster(); 
    components = new ArrayList<ItemComponentManager>(); 
    } 

    ... 
} 

Der Punkt in erster Linie ist die langwierige Prüfung für null, bevor Sie eine Instanz Variable irgendwo im Code zu vermeiden. Bis jetzt funktioniert es gut und Sie brauchen meistens nicht Null-Wert, wie Sie auch für leere Zeichenfolge oder leere Liste, etc. überprüfen können. Ich verwende diesen Ansatz für Method-Scoped-Variablen nicht, da ihr Umfang sehr begrenzt ist und wirkt sich daher nicht auf andere Teile des Codes aus.

Das alles ist irgendwie experimentell, also würde ich gerne wissen, ob dieser Ansatz funktionieren könnte oder ob es einige Fallstricke gibt, die ich noch nicht sehe. Ist es generell eine gute Idee, Instanzvariablen initialisiert zu halten?

+0

Erstellen Sie niemals eine leere ArrayList, nur um sie später wegzuwerfen. Verwenden Sie immer eine der Methoden in der Collections-Klasse, um eine leere, unveränderbare Auflistung abzurufen. –

+0

Danke für die Antworten bisher, habe ein paar wertvolle Beiträge erhalten. – MicSim

Antwort

9

Ich behandle in der Regel eine leere Sammlung und eine Null-Sammlung als zwei getrennte Dinge:

Eine leere Sammlung bedeutet, dass ich weiß, es gibt null Veröffentlichungen. Eine Null-Sammlung wird mir sagen, dass ich den Zustand der Sammlung nicht kenne, was eine andere Sache ist.

Also ich glaube wirklich nicht, dass es entweder/oder ist. Und ich würde die Variable endgültig deklarieren, wenn ich sie im Konstruktor initialisiere. Wenn Sie es als endgültig erklären, wird dem Leser klar, dass diese Sammlung nicht null sein kann.

0

Was passiert, wenn in einem Ihrer Methoden geschieht Sie setzen

itemMaster = null; 

oder Sie geben einen Hinweis auf die ItemManager zu einer anderen Klasse und setzt ItemMaster als null. (Sie können sich davor schützen, einen Klon von Ihrem ItemManager usw. zurückgeben)

Ich würde die Schecks behalten, wie dies möglich ist.

+0

Diese Nulleinstellung ist, was ich von Entwurf (als ungeschriebene Regel) vermeide: keine Methode wird jemals "itemMaster = null" aufrufen – MicSim

+0

Und man kann das Zurücksetzen der Referenz (für veränderbare Klassen) durch Verwendung von final erzwingen. – gimpf

+0

Externe Code-Einstellung, die Bezug auf itemMaster hat keine Wirkung. Es wird nur die lokale Referenz auf das Objekt entfernt. Es wird nicht die interne Referenz des Objekts löschen oder das Objekt zerstören. – Clayton

1

Ja, es ist eine gute Idee, alle Klassenvariablen im Konstruktor zu initialisieren.

0

Wenn es Ihr Code ist und Sie diese Konvention festlegen möchten, sollte es eine nette Sache sein. Ich stimme jedoch Pauls Kommentar zu, dass nichts daran hindert, dass ein fehlerhafter Code versehentlich eine Ihrer Klassenvariablen auf null setzt. In der Regel überprüfe ich immer auf Null. Ja, es ist ein PITA, aber defensive Codierung kann eine gute Sache sein.

3

Ich würde sagen, das ist völlig in Ordnung - solange Sie sich erinnern, dass Sie dort "leere" Platzhalterwerte haben und keine echten Daten.

Wenn Sie sie auf Null setzen, haben Sie den Vorteil, dass Sie sich mit ihnen auseinandersetzen müssen - andernfalls stürzt das Programm ab. Wenn Sie leere Objekte erstellen, diese jedoch vergessen, erhalten Sie undefinierte Ergebnisse.

Und nur um die defencive Codierung zu kommentieren - Wenn Sie derjenige sind, der die Objekte erstellt und sie niemals null setzt, ist es nicht notwendig, jedes Mal auf Null zu prüfen. Wenn Sie aus irgendeinem Grund Nullwert erhalten, dann wissen Sie, dass etwas katastrophal falsch gelaufen ist und das Programm trotzdem abstürzen sollte.

+0

Ich zweite die kritische Meinung über defensive Codierung. Allzu oft verbirgt "Defensive" nur Logikfehler, und ich habe oft Defensiv-Deals gesehen. Viel seltener hatte ich Probleme mit einer Fail-Fast-Haltung. – gimpf

+0

"defensiv" sollte keine logischen Fehler bei korrekter Fehlerberichterstattung verbergen. Wenn Sie nach null suchen und einen Fehler melden, wenn null auftritt, sind Sie defensiv und Sie können logische Fehler schnell und einfach erkennen, ohne eine Produktionsanwendung zu löschen. –

1

Der Punkt ist vor allem die mühsame Überprüfung auf null zu vermeiden, bevor eine Klassenvariable irgendwo im Code.

Sie müssen immer noch auf Null prüfen. Bibliotheken von Drittanbietern und sogar die Java-API geben manchmal null zurück.

Auch die Instanziierung eines Objekts, das niemals verwendet wird, ist verschwenderisch, aber das hängt vom Design Ihrer Klasse ab.

2

Ich würde sie final wenn möglich machen. Dann müssen sie im Konstruktor initialisiert werden und können nicht null werden.

Sie sollten sie auch in jedem Fall privat machen, um zu verhindern, dass andere Klassen ihnen null zuweisen. Wenn Sie überprüfen können, dass NULL in Ihrer Klasse niemals zugewiesen wird, funktioniert der Ansatz.

+0

Endgültig !? Dies erschwert die Ad-hoc-Serialisierung. Auf eine db zu beharren wird schwierig. Die Erweiterung der Klasse wird ebenfalls zu einem Schmerz. – Pat

+0

@Pat YAGNI (du wirst es nicht brauchen). Unterklassen können sowieso nicht auf den privaten Zustand einer Superklasse zugreifen, und das ist gut so. – eljenso

2

Ich bin auf einige Fälle gestoßen, in denen dies Probleme verursacht. Während der Deserialisierung werden einige Frameworks den Konstruktor nicht aufrufen, ich weiß nicht, wie oder warum sie dies tun, aber es passiert. Dies kann dazu führen, dass Ihre Werte null sind. Ich bin auch auf den Fall gestoßen, in dem der Konstruktor aufgerufen wird, aber aus irgendeinem Grund werden Mitgliedsvariablen nicht initialisiert.

In der Tat würde ich den folgenden Code anstelle von Ihnen verwenden:

public class ItemManager { 

    ItemMaster itemMaster = new ItemMaster(); 
    List<ItemComponentManager> components = new ArrayList<ItemComponentManager>(); 

    ItemManager() { 
    ... 
    } 
    ... 
} 
1

Ein Objekt sollte für die Verwendung 100% bereit sein, nachdem es aufgebaut ist. Benutzer sollten nicht auf Nullen achten müssen. Defensive Programmierung ist der Weg zu gehen - halten Sie die Kontrollen.

Im Interesse von DRY können Sie die Prüfungen in die Setter setzen und den Konstruktor einfach anrufen lassen. Auf diese Weise codieren Sie die Schecks nicht zweimal.

+0

Seien Sie vorsichtig beim Aufrufen von Setter aus einem Konstruktor - wenn eine Unterklasse die Setter überschreibt, können Sie am Ende Unterklassencode aufrufen, bevor die Unterklasse initialisiert wurde! –

+0

Aber wenn ich in Konstruktor der Klasse A bin, und es seine eigenen Setter aufruft, wie ist eine Unterklasse B wichtig, die sie außer Kraft setzt? "this" zeigt auf eine Instanz von A, also würde ich A-Setter in seinem Konstruktor aufrufen, nicht B. True? – duffymo

+0

@duffymo 'Dies' in einem Konstruktor bezieht sich immer auf das zu konstruierende Objekt. Der statische Typ von 'Dies' ist die Klasse, in der er erscheint. Der Laufzeittyp kann dieselbe Klasse oder jede Unterklasse dieser Klasse sein. – eljenso

4

In erster Linie alle nicht letztinstanzlich Variablen müssen privaten deklariert werden, wenn Sie die Kontrolle behalten wollen!

Betrachten faul Instanziierung als auch - dies vermeidet auch „schlechter Zustand“, sondern initialisiert nur bei der Verwendung:

class Foo { 
    private List<X> stuff; 
    public void add(X x) { 
     if (stuff == null) 
      stuff = new ArrayList<X>(); 
     stuff.add(x); 
    } 
    public List<X> getStuff() { 
     if (stuff == null) 
      return Collections.emptyList(); 
     return Collections.unmodifiableList(stuff); 
    } 
} 

(Beachten Sie die Verwendung von Collections.unmodifiableList - es sei denn, Sie wirklich ein Anrufer in der Lage sein wollen (Hinzufügen/Entfernen von Ihrer Liste, sollten Sie es unveränderlich machen)

Denken Sie darüber nach, wie viele Instanzen des betreffenden Objekts erstellt werden. Wenn es viele gibt und Sie immer die Listen erstellen (und möglicherweise mit vielen leeren Listen enden), könnten Sie viel mehr Objekte erstellen, als Sie benötigen.

Abgesehen davon, es ist wirklich eine Frage des Geschmacks und wenn Sie sinnvolle Werte haben, wenn Sie konstruieren.

Wenn Sie mit einem DI/IOC arbeiten, wollen Sie den Rahmen für Sie die Arbeit tun (auch wenn man es durch Konstruktor Injektion tun könnte, ich ziehe Setter) - Scott

+0

Wirklich, Sie denken, dass alle Mitgliedsvariablen privat sein sollten? Geschützt, ja. Privat, nein. Was ist mit Unterklassen? –

+0

Unterklassen könnten sie auf null setzen. – recursive

+0

Instanzfelder sollten für eine ordnungsgemäße Kapselung privat sein, niemals geschützt. – eljenso

2

So wie ich mit jeder Variablen umgehen, die ich deklariere, ist zu entscheiden, ob sie sich über die Lebensdauer des Objekts (oder der Klasse, wenn es statisch ist) ändern wird.Wenn die Antwort "Nein" ist, mache ich es endgültig.

So dass es endgültige zwingen Sie ihm einen Wert geben, wenn das Objekt erstellt wird ... ich persönlich würde folgende tun, wenn ich weiß, dass ich würde ändern, was den Punkt, an:

 
    private final ItemMaster itemMaster; 
    private final List components; 

    // instance initialization block - happens at construction time 
    { 
     itemMaster = new ItemMaster(); 
     components = new ArrayList(); 
    } 

Die Art und Weise Ihr Code befindet sich gerade jetzt, Sie müssen die ganze Zeit auf Null überprüfen, da Sie die Variablen nicht als privat markiert haben (was bedeutet, dass jede Klasse im selben Paket die Werte auf null setzen kann).

+0

Oder nur private final ItemMaster itemMaster = neue ItemMaster(); private final Liste Komponenten = new ArrayList(); –

+0

Ich möchte die Initialisierung getrennt von der Deklaration halten. – TofuBeer

0

Vom Namen der Klasse "ItemManager" klingt ItemManager in einigen Apps wie ein Singleton. Wenn ja, sollten Sie untersuchen und wirklich, wirklich, Dependency Injection kennen. Verwenden Sie etwas wie Spring (http://www.springsource.org/), um die Liste von ItemComponentManagers in ItemManager zu erstellen und zu injizieren.

Ohne DI, Initialisierung von Hand in ernsthaften Anwendungen ist ein Albtraum zu debuggen und verbinden verschiedene "Manager" -Klassen, um enge Tests ist die Hölle.

Verwenden Sie DI immer (auch beim Erstellen von Tests). Erstellen Sie für Datenobjekte eine Methode get(), die die Liste erstellt, falls sie nicht existiert. Wenn das Objekt jedoch komplex ist, wird es Ihr Leben mit dem Factory- oder Builder-Muster mit ziemlicher Sicherheit besser finden und das F/B-System die Mitgliedsvariablen nach Bedarf festlegen.

Verwandte Themen