Endgültige Methoden zum Initialisieren einer Instanzvariablen verwenden

Von der Sun-Dokumentation

Normalerweise würden Sie Code zum Initialisieren einer Instanzvariable in einem Konstruktor verwenden.
Es gibt zwei Alternativen zur Verwendung eines Konstruktors zum Initialisieren von Instanzvariablen: Initialisieren von Blöcken und endgültigen Methoden.

Ich könnte die Verwendung von Initialize-Blöcken verstehen. Kann jemand bitte die Verwendung von finalen Methoden für die Var-Initialisierung erklären? Ein nicht finaler öffentlicher Setter kann diese Aufgabe übernehmen. Warum benutzt man sie nicht einfach?

Der Vorteil ist bereits im selben Sun-Tutorial beschrieben, mit dem Sie verlinkt haben:

Eine endgültige Methode kann in einer Unterklasse nicht überschrieben werden. Dies wird in der Lektion zu Schnittstellen und inheritance erläutert.

Dies ist besonders nützlich, wenn Unterklassen die Initialisierungsmethode möglicherweise erneut verwenden möchten. Die Methode ist endgültig, da das Aufrufen nicht endgültiger Methoden während der Instanzinitialisierung Probleme verursachen kann. Joshua Bloch beschreibt dies ausführlicher in Effektivem Java (Punkt 17 Design und Dokument für die inheritance) .

Der Grund, warum eine nicht endgültige Methode bei der Initialisierung gefährlich ist, liegt darin, dass die Instanzinitialisierung der Oberklasse ausgeführt wird, bevor die Unterklasse initialisiert wird. Wenn daher die nicht-finale Methode in der Unterklasse überschrieben wird und während der Initialisierung der Oberklasse ausgeführt wird, kann sie auf nicht initialisierte Felder der Unterklasse zugreifen, was zu errorshaften Ergebnissen führt.

Die allgemeine Regel lautet (Zitat aus Effektivem Java): Konstruktoren dürfen keine überschreibbaren Methoden direkt oder indirekt aufrufen.

Es wird auf der gleichen Seite des referenzierten Tutorials erklärt. Der Grund dafür ist, dass eine nicht finale Methode von der übergeordneten class überschrieben werden kann. Hier ist ein Beispiel:

class Whatever { private List myVar = initializeInstanceVariable(); protected List initializeInstanceVariable() { return new ArrayList(); } } class Whoever extends Whatever { @Override protected List initializeInstanceVariable() { return Collections.unmodifiableList(super.initializeInstanceVariable()); } } 

Wenn du also wer auch immer erstellst, wird myVar unmodifizierbar 😉

Andere Beispiele

Von alykhantejani.github.io

Ich habe es kompilierbar und vereinfacht.

Ente.java

 public class Duck { String sound = "quack"; protected String speech; public Duck() { initSpeech(); } protected void initSpeech() { speech = "sound = " + sound; } public void speak() { System.out.println(speech); } protected String getSound() { return sound; } } 

Quietschenduck

 public class SqueakyDuck extends Duck { String squeakSound = "squeak"; public SqueakyDuck() { super(); } @Override protected void initSpeech() { speech = "sound = " + squeakSound; } @Override protected String getSound() { return squeakSound; } } 

Haupt.java

 public class Main { public static void main(String[] args){ Duck squeaky = new SqueakyDuck(); squeaky.speak(); System.out.println(squeaky.getSound()); } } 

Ausgabe:

 sound = null squeak 

Mein Beispiel

Superklasse.java

 public class Superclass { protected int x = m(); protected int m() { return 8; } } 

Unterklasse.java

 public class Subclass extends Superclass { private int y = 7; @Override protected int m() { return y; } } 

Haupt.java

 public class Main { public static void main(String[] args) { Superclass s = new Subclass(); System.out.println(sx); } } 

Ausgabe:

 0 

Reihenfolge der Ausführung:

  • main
  • m von Subclass ( y wird in diesem Moment nicht initialisiert und 0 ist der Standardwert für int )
  • Konstruktor Superclass
  • Konstruktor Subclass