Unerklärliches Verhalten bei Variableninitialisierung

Status
Nicht offen für weitere Antworten.

mohrenkopf

Mitglied
Ich weiß wirklich nicht mehr weiter, habe aus einem Projekt mal den folgenden Code extrahiert und kann in keinster Weise verstehen, wie es zu diesem Output kommen soll...

Ich kann es mir nur so erklären, dass die init()-Methode der Subklasse sozusagen "wärend" der Anweisung "
Code:
private int var=50
ausgeführt wird und zwar nach der deklaration (sonst würde this.var=100 ja wohl schlecht funktionieren), aber vor der Wertzuweisung =50 (denn am Schluss hat die Variable ja den Wert 50).



Der Programmoutput ist
run-single:
0
100
50
BUILD SUCCESSFUL (total time: 4 seconds)
Der Spaß: Wenn ich der Variablen keinen initialen Wert zuweise (also nur
Code:
private int var;
schreibe, dann ist der Output 0,100,100.

Bin für jeden Rat dankbar:bahnhof:
mohrenkopf



Code:
class Superclass
{
    /**Konstruktor */
    public Superclass()
    {
        init();
    }

    public void init()
    {
        //Nur zur überprüfung, dass dieser Code hier nicht ausgeführt wird
        throw new UnsupportedOperationException("Override me!");
    }

}
class Subclass extends Superclass
{
    private int var = 50;     //die fragliche Variable
   
    @Override
    public void init()
    {
        output();                 //erzeugt als 1. Output 0
        this.var=100;
        output();                 //erzeugt als 2. Output 100
    }

    public void output()
    {
        System.out.println(this.var);     //3. Output: liefert dann 50
    }
}

public class WhatHappensHere2
{
    public static void main(String[] args)
    {
        Subclass s = new Subclass();
        s.output();
    }
}
 

mohrenkopf

Mitglied
Danke für den Tipp, aber das hilft mir jetzt nicht wirklich weiter.

Kannst du mit bitte verraten, warum man das nicht soll/darf?

Zumal das doch bei JFrame Forms gang und gäbe ist, initComponents() im Konstruktor aufzurufen.
 

Wildcard

Top Contributor
Um ein Objekt instanzieren zu können muss zunächst der super Konstruktor durchlaufen werden, dann die eigenen Felder initialisiert werden und dann der eigene Konstruktor durchlaufen werden. Ruft die Superklasse im Konstruktor nun eine non-final und evtl. überschriebene Methode auf, ist die Klasse nicht korrekt initialisiert und daher in womöglich inkonsitentem Zustand.
Kannst du mit bitte verraten, warum man das nicht soll/darf?
Weil man dann ein 'Unerklärliches Verhalten bei Variableninitialisierung' hat und in einem Forum nachfragen muss ;)
 

mohrenkopf

Mitglied
Wieder was gelernt!


Nur damit ich das richtig verstehe:
Beim instantiieren von Subclass passiert das folgende
1. Ein Subclass-Objekt wird erzeugt, alle Variablen werden deklariert (ABER: zunächst hat die Variable den Wert 0)
2. Der Konstruktor von Subclass wird aufgerufen. (der hier ja unsichtbar vorhanden ist)
3. Der Konstruktor von Subclass ruft als erstes automatisch mal den Konstruktor von Superclass auf.
4. Dieser wiederum ruft init() auf, init() wurde aber überschrieben, somit wird die init() der Subclass benutzt.
5. Die Variable erhält den Wert 100
6. Der Superclass Konstruktor ist fertig
7. Nun werden alle Felder der Subclass mit den initialen Werten belegt, d.h. jetzt erst wird der zweite Teil var=50; ausgeführt.
8. Dann würde der (hier nicht vorhandene) Body des Subclass-Konstruktors ausgeführt.


Wenn's so ist, dann leuchtet mir das ein! Danke für deine Antwort


Was bietet sich denn dann als Lösung an, wenn ich die init()-Methode aufrufen muss? Statische createInstance()-Methode, die eine Instanz erzeugt, init() aufruft und dann die Ref zurückgibt? Oder besser gleich Factory etc...?
 
Zuletzt bearbeitet:

Ebenius

Top Contributor
Niemals im Konstruktor einer nicht finalen Klasse eine nicht finale Methode aufrufen.
Das halte ich für ein falsches Paradigma... In vielen Fällen gibt dieses Konstrukt Sinn; halb Swing ist so aufgebaut: [HIGHLIGHT="Java"]public JList(ListModel dataModel)
{
// .....
selectionModel = createSelectionModel(); // <== protected; nicht final
setAutoscrolls(true);
setOpaque(true);
updateUI(); // <== public; nicht final
}[/HIGHLIGHT]
Erstere Methode ist sogar explizit dafür gedacht überschrieben zu werden.

Ebenius
 

mohrenkopf

Mitglied

Das vermeidet sicherlich solche "unerklärlichen Dinge", die eigentlich ganz logisch sind, wenn man es sich genau überlegt bzw. einen Hinweis darauf bekommt ;)

Der Link nennt ja als Lösung
The best way to satisfy both issues is to make the superclass abstract and require (through documentation) that all subclasses enforce the invariant in their constructor.
Ob "per documentation" so sauber ist?

Genauso hätte wohl der Ansatz "Keine Wertzuweisung bei der Deklaration von Attributen sondern erst im Konstruktor" geholfen, dabei wäre genau das selbe passiert, ich hätte aber wohl gemerkt, warum.


Es muss doch eine elegante Lösung geben, eine initialize()-Methode einer Subklasse automatisch bei der Instantiierung aufzurufen...
 

Ebenius

Top Contributor
Über den Artikel würde ich mich mit dem Autor gern bei nem Bier diskutieren. :) Das geschilderte Problem existiert und darüber herrscht bei mir kein Diskussionsbedarf.

Der Lösungsvorschlag des Autors ist jedoch in meinen Augen unbrauchbar. Er würde erstklassisch funktionieren, wenn die SuperClass verlangen würde, dass SubClass final sein müsse oder computeMessage(FooBar x) in SubClass final überschrieben werden würde. Diese Einschränkung würde für meinen Geschmack aber viel zu weit gehen. In jedem anderen Fall hätte eine von SubClass erbende SubSubClass dasselbe Problem wieder und außer Schreibaufwand wäre nichts gewonnen.

Für das Beispiel einer Swing-Komponente entfällt der vom Autor erzeugte Lösungsvorschlag ohnehin, da Swing-Komponenten kaum abstrakt sein können. Der Aufbau der Initialisierung in JList (siehe oben) oder auch JTable kann selbstverständlich zu Problemen führen, wenn man von der Klasse ableitet und nicht weiß, auf welchem Weg die createXXX()-Methode aufgerufen wird. Diesem Problem wäre mit einem Hinweis für Ableitungen in der Schnittstellenbeschreibung der jeweiligen Methode abgeholfen.

PS: Ich kann mir Namen immer so schlecht merken. Ist Ben Pryor jemand den ich kennen und ernst nehmen sollte?

Ebenius
 
Zuletzt bearbeitet:

Wildcard

Top Contributor
Eine Lösung ist zB über die Dokumentation bekanntzugeben, dass der Client initialize aufrufen muss. Tut er das nicht, und versucht das Objekt zu verwenden, wirft es eine NotInitializedException.
Jede mir bekannte Lösung für dieses Problem hat Nachteile, aber die Seiteneffekte die Auftreten können sind meiner Meinung nach schlimmer als die Nachteile durch einen Workaround. Ich will jetzt nicht behaupten noch niemals in einem Konstruktor eine nicht-finale Methode aufgerufen zu haben, aber ich denke wirklich, man sollte es nicht tun.
 
Status
Nicht offen für weitere Antworten.
Ähnliche Java Themen
  Titel Forum Antworten Datum
G unerklärliches System.out Allgemeine Java-Themen 3
isowiz Unerklärliches Problem mit Math.pow :( Allgemeine Java-Themen 4
javamax2000 Sehr sonderbares Verhalten Allgemeine Java-Themen 6
kodela Unterschiedliches Verhalten von BufferedReader Allgemeine Java-Themen 3
J Unvorhersehbares Verhalten - benutze ich die falsche Bedingungsprüfung oder brauche ich Threads? Allgemeine Java-Themen 12
N Best Practice Allgemeines Verhalten für ein Interface implementieren? Allgemeine Java-Themen 7
Thallius Merkwürdiges Verhalten von Swingworker.cancel() Allgemeine Java-Themen 2
T Merkwürdiges Thread-Verhalten Allgemeine Java-Themen 6
Tommy Nightmare Merkwürdiges Verhalten bei der Datenzuweisung Allgemeine Java-Themen 4
F JTable Pfeiltasten-Verhalten Allgemeine Java-Themen 1
Thallius Swing Merkwürdiges Verhalten beim Panel Tausch Allgemeine Java-Themen 3
W LocalDateTime Verhalten unerklärlich Allgemeine Java-Themen 1
Thallius Merkwürdiges StringBuilder verhalten (Char Encoding) Allgemeine Java-Themen 6
C Unterschiedliches Verhalten Editor und deployte Application Allgemeine Java-Themen 3
S Threads ThreadPoolExecutor eigenartiges verhalten Allgemeine Java-Themen 5
A Java Verhalten bei parallelem Aufruf derselben Methode?? Allgemeine Java-Themen 2
P Applet-Zugriffsrechte: merkwürdiges Verhalten Allgemeine Java-Themen 4
M Threads Viele Aufrufe aus Thread, komisches Verhalten Allgemeine Java-Themen 8
S getChildAt() Verhalten Allgemeine Java-Themen 4
S Frage zu Threads (Sichtbarkeit und Verhalten) Allgemeine Java-Themen 11
R Merkwürdiges Verhalten der equals Method Allgemeine Java-Themen 4
1 Collections Generics, internes Verhalten Allgemeine Java-Themen 16
S Collections Unverständliches Verhalten... Allgemeine Java-Themen 4
M Nach Programmdurchlauf werden Zeichen falsch dargestellt + Anderes Verhalten unter Windows Allgemeine Java-Themen 6
C Komisches Verhalten zwischen Set und List bei contains Allgemeine Java-Themen 6
S (Doppel)Klick-Verhalten vom Desktop unter Java imitieren. Allgemeine Java-Themen 5
A Seltsames Verhalten von JUnit-Tests im Zusammenspiel mit Ant Allgemeine Java-Themen 6
S Verhalten von System.getenv() in Ubuntu / Linux Allgemeine Java-Themen 12
hdi Verhalten bei nicht behandelten Exceptions Allgemeine Java-Themen 2
J Rätselhaftes Verhalten von Collections Allgemeine Java-Themen 5
S Verhalten der Klasse TreeSet... Allgemeine Java-Themen 4
S Jar und Exe verhalten sich unterschiedlich unter Vista Allgemeine Java-Themen 8
M Seltsames Verhalten eines StringReaders Allgemeine Java-Themen 2
spacegaier HeapSpace der VM ändern -> Verhalten von JARs und EXEs Allgemeine Java-Themen 10
M merkwürdiges Verhalten von JUnit4 Allgemeine Java-Themen 2
M JList seltsames verhalten. Allgemeine Java-Themen 5
K Seltsames Verhalten von byte[] und Strings Allgemeine Java-Themen 6
S Seltsames Verhalten von split() Allgemeine Java-Themen 3
K Überschreiben von 'static'-Methoden hat anderes Verhalten? Allgemeine Java-Themen 2
A Streams: merkwürdiges Verhalten Allgemeine Java-Themen 7
A Streams - merkwürdiges Verhalten Allgemeine Java-Themen 2

Ähnliche Java Themen

Neue Themen


Oben