Und eine Klasse B, die A erweitert mit einer ArrayList als feld darin:
Java:
publicclassBextendsA{publicstaticArrayList<C> liste =newArrayList<C>(15);publicB(){super();}publicvoidonInit(){
liste.add(newC());}}
In Zeile 12 von Klasse B bekomme ich eine NullPointerException.
Durch eine simple if-Abfrage habe ich bereits bestätigt, dass liste an der Stelle null ist.
Ich würds auch nicht glauben, wenns nicht so wäre -_-
Ich geb euch mal den echten Code, ist aber wahrscheinlich schwerer zu lesen:
Java:
publicabstractclassNeoScreenextendsGenericPopup{//Hier fehlen natuerlich noch ein paar sachenpublicNeoScreen(SpoutPlayer player){
width = player.getMainScreen().getWidth();
height = player.getMainScreen().getHeight();this.player = player;this.onInit();}//Und hier sind auch noch ein paar unwichtige methodenpublicabstractvoidonInit();}
Java:
publicclassGuiNPCextendsNeoScreen{privateArrayList<Label> messages =newArrayList<Label>(15);//Weitere deklarationenpublicGuiNPC(SpoutPlayer player){super(player);}@OverridepublicvoidonInit(){addButton(0,(width -73)/2,(height +166)/2,75,10,"Close");for(int i =0; i <15; i++){Label label =addLabel(0,0,"");
label.setX((width -GenericLabel.getStringWidth(label.getText()))/2);
label.setY(height /6+ i *10);
messages.add(label);//Hier kommt die exception}}//Weitere methoden}
Achja, der fehler kommt beim Instanzieren von B bzw. GuiNPC
Hier ist messages / deine Liste ja auch nicht statisch, das ist ein ganz anderes Beispiel jetzt. Du rufst hier schon Methoden auf (init) und dein Objekt ist noch nicht einmal instanziiert 100%. Das ist unschön!
eRaaaa hat schon die Lösung gepostet!
Das Objekt ist noch nicht vollständig initialisiert wenn du die Methode onInit aufrufst. Als Faustregel: im Konstruktor nie nicht-finale Methoden aufrufen. Speziell wenn die Klasse abgeleitet wird.
Oh verdammt...
Wie kann ich onInit denn direkt nach dem initialisieren aufrufen?
Ich möchte nicht, dass man die Methode jedes mal von Hand aufrufen muss...
deklarationen können auch im static-context stehen ...
initialiserungen MÜSSEN in methoden/konstruktoren stehen ...
auch in diesem beispiel KANN "liste" NULL sein ... wenn der compiler das "new List<?>()" erst hinter den call von "onInit()" schiebt ...
was durch den call von "super()" immer der fall sein wird ... da "super()" grundsätzlich IMMER zu erst kommen muss ...
wenn nun durch diesen super-call eine methode gecallt wird die ein objekt verändern soll ... was so noch garnicht exisitiert *da die initialisierung erst NACH super() kommen würde* ist das objekt *oder besser die referenz* NULL
ums mal zu veranschaulichen
deinen pseudo-code würde die VM wie folgt übersetzen :
Java:
publicclassBextendsA{publicstaticArrayList<C> liste;publicB(){super();
liste =newArrayList<C>(15)}publicvoidonInit(){
liste.add(newC());}}
die call-reihenfolge wäre aber : B() -> super() -> A() -> onInit() -> liste.add() -> liste=new List<?>
und aus genau diesem grund bekommst du die NPE
Du weißt schon wann statische Felder initialisiert werden? Dann wenn die Klasse geladen wird, und das ist garantiert bevor der Konstruktor aufgerufen wird, denn erst nach dem Laden weiß die VM welche Konstruktoren es gibt.
Natürlich hast du recht dass Initialisierungen in Methoden stehen müssen. Aber der Compiler ist so nett und macht aus:
Java:
classFoo{String bar ="Test";}
Java:
classFoo{String bar;void<init>(){
bar ="Test";}}
Und aus:
Java:
classFoo{staticString bar ="Test";}
Java:
classFoo{staticString bar;staticvoid<clinit>(){
bar ="Test";}}
Da hakt es aber gewaltig...
Was "irgendjemand" da sagt, ist nur teilweise richtig und für ...ButAlive gilt das selbe.
nicht primitive Klassenkonstanten werden unter Umständen erst bei ihrer ersten Verwendung in Konstruktoren instanziert. Wenn man sie nicht explizit in der Klasseninitialisierungs-Methode ([c]static {}[/c]) initialisiert, bekommt man möglicherweise solche Probleme, wie der TO (er ruft eine Klassen-Methode auf).
Im Übrigen lassen sich (vorzugsweise bei Singletons) Konstruktoren auch schon bei der Initialisierung der Klasse aufrufen... das ist böse. XD
nicht primitive Klassenkonstanten werden unter Umständen erst bei ihrer ersten Verwendung in Konstruktoren instanziert.
Wenn man sie nicht explizit in der Klasseninitialisierungs-Methode ([c]static {}[/c]) initialisiert, bekommt man möglicherweise solche Probleme, wie der TO (er ruft eine Klassen-Methode auf).
A static initializer declared in a class is executed when the class is initialized (§12.4.2). Together with any field initializers for class variables (§8.3.2), static initializers may be used to initialize the class variables of the class.
So viel anders ist das gar nicht. Der statische Initializer muss implementiert sein, damit das zutrifft. Ich bin auf diese JVM-Meise auch schon reingefallen. Solange dort nicht "final" steht und static nicht implementiert wurde, instanziert die JVM on demand (hast's in deinen Codebeispielen doch erklärt).
Irgendwie reden wir aneinander vorbei. Wenn man eine statische Variable instanziert ohne einen static initzialzier Block zu schreiben, erzeugt der Compiler einen. Man muss diesen nicht explizit angeben. Dieser wird dann ausgeführt wenn die Klasse instanziert wird. Das ist der Fall, wenn der Classloader sie zum ersten mal lädt.
Bevor man den Konstruktor einer Klasse aufrufen kann, ist garantiert die Klasse schon geladen.
Geladen ja, aber initialisiert? Nicht unbedingt. Konstrukte wie
Java:
classAnyClass{staticAnyClass instance;static{
instance =newAnyClass();}AnyClass(){// some code}}
funktionieren zwar, aber man muss genau wissen, was man wann tut, sonst kommt der Hangman. Z.B. braucht man sich nicht einfallen lassen, innerhalb des Konstruktors auf "instance" zugreifen zu wollen. Eigentlich sollten solche Konstrukte verboten werden, damit man einen Mechanismus hinbekommt, der sicherstellt, dass eine Klasse vollständig initialisiert wurde, noch bevor der Konstruktor aufgerufen werden kann. Wie will man denn sonst wissen, wann eine Klasse spätestens initialisiert ist bzw. sein sollte? Wie gesagt: Die von der VM automatisch eingefügten initializer werden irgendwo mitten bei der ersten Instanzierung aufgerufen.