Variablen im Konstruktor oder Rumpf der Klasse initialisieren?
-
Macht es einen Unterschied ob ich die 1. oder die 2. Variante verwende? Welche ist zu bevorzugen? Spielt es dabei eine Rolle ob es sich um einen einfachen Datentypen oder um ein Objekt vom Typ einer Klasse handelt?
public class Robot { private int functions = 3; public Robot() { } }
oder
public class Robot { private int functions; public Robot() { functions = 3; } }
-
Macht erst mal keinen Unterschied, ist wohl auch etwas Geschmackssache, ich würde aber dennoch zu letzter Lösung tendieren (also Initialiserung im Konstruktor), da so viel klarer ist was passiert bei der Erstellung des Objeckts (auch gerade bei Vererbungsgeschichten).
Auch wenn du eine Konstruktor-Verkettung machen willst (d.h. du hast mehrere Konstruktoren und rufst innerhalb eines Konstruktors einen anderen "einfacheren" Konstruktor auf) ist letzteres besser.
-
smax schrieb:
Spielt es dabei eine Rolle ob es sich um einen einfachen Datentypen oder um ein Objekt vom Typ einer Klasse handelt?
Nein!
-
Es macht dann einen Unterschied wenn du mehrere Konstruktoren hast. Dann müsstest du die Initialisierung in allen Konstruktoren hinschreiben, wenn du sie aber gleich bei der Initialisierung machst nur einmal. Es ist üblich Werte, die nicht von Parametern der (meisten) Konstruktoren der Klasse abhängig sind direkt bei der Deklaration zu initialisieren.
Das hier:
public class A extends B { int val = 3; public A() { System.out.println("very first statement?"); } public A( Object obj ) { System.out.println("very first statement?"); } }
ist semantisch im Prinzip das gleiche wie das hier:
public class A extends B { int val; // erstmal automatisch mit 0 vorbelegt; public A() { super(); val = 3; System.out.println( "very first statement?" ); } public A( Object obj ) { super(); val = 3; System.out.println( "very first statement?" ); } }
... weil der Compiler für beide Varianten den gleichen Bytecode erzeugt
Die Zuweisungen aus den Deklarationen werden vom Compiler also automatisch zwischen den Aufruf des Superkonstruktors und den ersten Befehl im Konstruktor gepackt. Dass damit die Variable im Prinzip zwei mal zugewiesen wird (alle Klassen- und Instanz-Variablen werden mit 0 bzw. null "geboren"), kannst du an diesem Programm leicht ausprobieren:
//-- A.java ---- public class A extends B { int val = 3; A() { printVal(); } void printVal() { System.out.println( "val=" + val ); } public static void main( String[] args ) { new A(); } } abstract class B { B() { printVal(); } abstract void printVal(); } //------
... gibt:
val=0
val=3Bei final Variablen versucht der Compiler im Gegensatz dazu allerdings die Zuweisung so früh wie möglich zu machen:
//-- A.java ---- public class A extends B { final int val = 3; A() { printVal(); } void printVal() { System.out.println( "val=" + val ); } public static void main( String[] args ) { new A(); } } abstract class B { B() { printVal(); } abstract void printVal(); } //------
... gibt:
val=3
val=3Und hier macht es dann auch einen sematischen Unterschied, ob du die Zuweisung direkt bei der Initialisierung machst oder erst im Konstruktor:
//-- A.java ---- public class A extends B { final int val; A() { val = 3; printVal(); } void printVal() { System.out.println( "val=" + val ); } public static void main( String[] args ) { new A(); } } abstract class B { B() { printVal(); } abstract void printVal(); } //------
... gibt:
val=0
val=3