Seltsames Verhalten...

Hallo 🙂

ich habe gerade beim Programmieren n kleines Phänomen. Ich poste mal ne "vereinfachte Version".

Java:
public abstract class TestAbstract1 {
	public TestAbstract1(){
		init();
	}
	protected abstract void init();
}

Java:
public abstract class TestAbstract2 extends TestAbstract1 {
	String test = "not changed";
	public TestAbstract2(){
		super();
	}
	protected void init() {
		test = "changed";
		System.out.println("changed test");
	}
}

Java:
public class TestClass extends TestAbstract2{
	public TestClass(){
		super();
		System.out.println("check 1: "+test);
	}
}

Java:
public class Test {
	public static void main(String[] args) {
		TestClass t = new TestClass();
		System.out.println("check 2: "+t.test);
	}
}

Wenn ich jetzt die main ausführe bekomme ich folgendes log:

changed test
check 1: not changed
check 2: not changed

Es ist als wenn das in folgender Reihenfolge ausgeführt wird:

1. String test = null;
2. [constructor -> test = "changed";]
3. test = "not changed";

Obwohl es meines Wissens nach so ablaufen müsste:

1. String test = "not changed"
2. [constructor -> test = "changed;]


Ist dieses Verhalten normal?
Es ist doch normalerweise z.b. sowas möglich:

Java:
ArrayList<String> liste = new ArrayList<>();
public Test2(){
	liste.add("test");
}

Warum hier nicht? Hier würde ich ne NPE erhalten weil liste null ist.
 
Zuletzt bearbeitet:
Zuletzt bearbeitet:
Ganz, ganz schlechte Idee so einen Code zu schreiben. Damit kann man sich unglaublich viele Probleme einhandeln, deshalb lieber lassen. Auf die Schnelle hier eine kurze Erläuterung java - What's wrong with overridable method calls in constructors? - Stack Overflow.

Oh, das ist die Erklärung! Eigentlich ganz logisch. Ich hatte vermutet die Variablen werden auch in den Subklassen zuerst definiert bevor der Superklassenkonstruktor durchläuft, aber da habe ich mich wohl getäuscht. Könnte man das dann so machen?
in TestAbstract1:
Java:
public static TestAbstract1 getInstance(Class <? extends TestAbstract1> subclass){
	TestAbstract1 t = subclass.newInstance();
	t.init();
}
dementsprechend in Test
Java:
public static void main(String[] args) {
	TestClass t = (TestClass) TestAbstract1.getInstance(TestClass.class);
	[...]
}
 
Zuletzt bearbeitet:

Könnte man, aber zuerst solltest du wissen das newInstance auch merkwürdiges Verhalten verursachen kann. Es ist so eine Art Wurmloch für Exceptions, im Programm taucht eine auf aber du hast keinen Hinweis darauf das es durch newInstance verursacht wird.

Der bessere Weg wäre
Java:
 public static <T extends TestAbstract1> T getInstance(Class<T> subclass)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        T t = subclass.getDeclaredConstructor().newInstance();
        t.init();
        return t;
    }

Falls du dich wunderst weshalb ich getDeclaredConstructor(...) verwende anstelle von getConstructor(...), die erste Methode ignoriert die Zugriffsmodifkatoren (public, private usw.) letztere nicht. Wobei ich solange es geht die "strengere" Variante bevorzugen würde, hier aber exemplarisch mal die andere gewählt habe.

Na jedenfalls erlaubt die obige Implementierung auf die unterschiedlichen Fehler besser zu reagieren. Für mehr Ausführungen wg. newInstance und eine nebenbei ganz nette Einführung in die Grundlagen von Java-Reflection kann sich folgendes Video angesehen werden. The Live Reflection Madness on Vimeo Bei Minute 27 rum fängt das Bsp. zu newInstance an.
 
Zuletzt bearbeitet:

Zurück
Oben