Initialisierte ArrayList wirft NullPointerException aus

Hallöle,

ich habe bei meinem bisherigen Kartenspiel eine sehr unvorteilhafte Variante der Variablenspeicherung gehabt. Es existieren zwei Spieler und die Variablen für Spielername, Punktzahl und Karten auf der Hand existierte für jeden Spieler einzeln.

Ungefähr so hier:

    private static String spielername1 = "";
    private static int punktespieler0 = 0;
    private static int punktespieler1 = 0;
    private static List<Gastkarte> kartenspieler0 = new ArrayList<Gastkarte>();
    private static List<Gastkarte> kartenspieler1 = new ArrayList<Gastkarte>();```

Das funktionierte bisher auch wunderbar. Mit entsprechenden Gettern und Settern konnte ich dann alles verstellen.

Nun aber habe ich mir gedacht, ich mache einfach eine Klasse Spieler mit diesen Eigenschaften und erstelle davon zwei Spieler.
So weit so gut, so sieht meine Spielerklasse aus:
```package spiel;

import java.util.ArrayList;
import java.util.List;

public class Spieler {
	
	private String name;
    private int punkte = 0;
    private List<Gastkarte> handkarten = new ArrayList<Gastkarte>();

	public Spieler() {
		
    }

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getPunkte() {
		return punkte;
	}

	public void setPunkte(int punkte) {
		this.punkte = punkte;
	}

	public List<Gastkarte> getHandkarten() {
		return handkarten;
	}

	public void setHandkarten(ArrayList<Gastkarte> handkarten) {
		this.handkarten = handkarten;
	}

}```

Danach habe ich zwei Spieler erstellt:
```private Spieler[] spieler = new Spieler[2];
spieler[0] = new Spieler();
        spieler[1] = new Spieler();```

Als Array gespeichert, damit ich einfach immer nur spieler[num].irgendwas schreiben muss, weil in int num die Nummer des aktuellen Spielers gespeichert ist.
So weit so gut. Aber es gibt ein Problem mit der ArrayList.
Ich kann jederzeit mit `spieler[0].getName()` oder `spieler[0].getPunkte()` den Namen und die Punktzahl abrufen. Aber jedes mal, wenn ich irgendetwas mit der ArrayList für die Handkaren machen möchte, kommt eine **NullpointerException** für diese Zeile.

`System.out.println(spieler[0].getHandkarten());`
`spieler[0].getSpieler(0).getHandkarten().add(new Gastkarte());`

Ich versteh das irgendwie nicht. Die ArrayList ist initialisiert und steht ansonsten in der Klasse Spieler nicht anders da als alle anderen Variablen, die aber funktionieren.

Sieht jemand den Fehler?

Gruß
Lukas

mit dahinter zwei Codezeilen…

was ist denn spieler[0].getSpieler(0) ?
wo ist eine Methode getSpieler() definiert, was liefert diese?

gib ruhig immer Zwischenwerte aus:
System.out.println("Test 1: "+spieler[0]);
→ wenn nicht null kommt, dann kann
System.out.println(spieler[0].getHandkarten());
kaum eine Exception geben

System.out.println("Test 2: "+spieler[0].getSpieler(0));
falls das bei kompiliert kommt da sicher null als Ausgabe?


folgendes vollständiges Testprogramm (du hast keins gepostet…) funktioniert natürlich ohne Exception:

public class Test2
{

    private Spieler[] spieler = new Spieler[2];

    public Test2()
    {
        spieler[0] = new Spieler();
        spieler[1] = new Spieler();

        System.out.println(spieler[0].getHandkarten());
        spieler[0].getHandkarten().add(new Gastkarte());
        System.out.println(spieler[0].getHandkarten());

    }

    public static void main(String[] args)
    {
        new Test2();
    }
}


class Spieler
{

    private String name;
    private int punkte = 0;
    private List<Gastkarte> handkarten = new ArrayList<Gastkarte>();

    public Spieler()
    {

    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public int getPunkte()
    {
        return punkte;
    }

    public void setPunkte(int punkte)
    {
        this.punkte = punkte;
    }

    public List<Gastkarte> getHandkarten()
    {
        return handkarten;
    }

    public void setHandkarten(ArrayList<Gastkarte> handkarten)
    {
        this.handkarten = handkarten;
    }

}

class Gastkarte
{
}

Uhu, ähm, ich habe mich vertippt.

Das hier gibt es nicht:
spieler[0].getSpieler(0).getHandkarten().add(new Gastkarte());
Das getSpieler muss da raus.
–> spieler[0].getHandkarten().add(new Gastkarte());
Weil spieler[0] ist ja bereits der Spieler.

Sehr richtige Idee, ich lasse mir nun mal folgendes ausgeben:

		System.out.println("Spieler1-Name: "+CafeMain.getSpieler(0).getName());
		System.out.println("Spieler1-Punktzahl: "+CafeMain.getSpieler(0).getPunkte());
		System.out.println("Spieler1-Handkarten: "+CafeMain.getSpieler(0).getHandkarten());```

Komischerweise kommt hier schon bei dem Spieler ein "null" raus. Verstehe ich ehrlich gesagt gerade nicht, weil das ja sonst keinen Sinn ergeben würde, dass bisher die Methoden getName und getPunkte funktioniert habe.
Aber okay. Try and Error.

Folgendes:
Ich habe mal mein gesamtes Programm auf GitHub hochgeladen, wenn das kein Problem hier sein sollte, auf externes zu verweisen. Sind inzwischen schon so viele Klassen, ehe ich hier 5 Tausend Zeilen Code in 20 Klassen poste und dann immer jemand fragen muss, was denn in Klasse xy steht, poste ich einfach mal alles.

Das ist mein Projekt:
https://github.com/Dabendorf/Caf-International-Grafik-/tree/master/src/spiel

Zur Erläuterung:
Die Hauptklasse mit main-Methode ist CafeMain. Dort ist auch in den Variablen oben das hier definiert:
```private static int aktspieler = 0;
    private static Spieler[] spieler = new Spieler[2];```

Der Array mit den Spielern und der Integer, der in den Array kommt, um den richtigen Spieler abzurufen, also beispielsweise: `spieler[aktspieler].getPunkte();`

Hier wird auch der Array sofort mit Inhalten befüllt:
```spieler[0] = new Spieler();
        spieler[1] = new Spieler();```

Die Methode `ablauf()` sorgt dann für den Restlichen Ablauf, den Du Dir chronologisch angucken kannst.
Überall wo `//Debug` dahinter steht, hab ich mir eine Methode geschraubt, um mir die Innereien des Programms genauer anzeigen zu lassen. Das ist für das Programm nicht relevant, kann man ignorieren. ;) Auch wenn man die Zeilen löscht, geht es nicht.

Der entscheidende Fehler tritt dann in der Klasse `Spielstart.gastkartenmischen()` auf, in der for-Schleife hinter den printlines, die ich gepostet habe.

Ich wäre sehr sehr dankbar, wenn irgendjemand herausfindet, woran das liegen kann.

Ansonsten läuft es echt spitze. :)

Schöne Grüße
Lukas

10 Min. Klassen kopieren für simplen Fehler,
bin selber schuld dass nicht vorher drauf gekommen, aber du solltest überlegen, ob du immer so einen Dummen findest oder nicht lieber anders vorgehen willst…


Exception in thread "main" java.lang.NullPointerException
	at test.Spielstart.gastkartenmischen(Test2.java:1353)
	at test.Spielfeld.feldmalen(Test2.java:349)
	at test.Spielfeld.<init>(Test2.java:313)
	at test.CafeMain.<init>(Test2.java:94)
	at test.Test2.main(Test2.java:52)

CafeMain-Konstruktor:

        spielframe.add(new Spielfeld(), new GridBagFelder(1, 0, 1, 2, 0.7, 1.0));   // hier tritt im weiteren Verlauf der Fehler auf
        spielframe.add(new Spielkartenecke(), new GridBagFelder(2, 0, 1, 1, 0.15, 0.5));
        spielframe.add(new Bildecke(), new GridBagFelder(2, 1, 1, 1, 0.15, 0.5));
        spielframe.pack();
        spielframe.setLocationRelativeTo(null);
        spieler[0] = new Spieler(); // hier werden erst die Spieler gesetzt
        spieler[1] = new Spieler();

wenn StackTrace nicht deutlich dann z.B. genau loggen,
beim Setzen der Spieler eine Ausgabe „Spieler wurden gesetzt“


diese Aussage ist dann ja wohl auch als Irreführung zu bezeichnen,
du kannst gar nix damit machen, das Programm startet ja nie, kommt nie soweit…

wieso hattest du speziell die ArrayList in Verdacht? hatte mit der ArrayList nichts zu tun, getName()-Abfrage ging genauso nicht

bin selber schuld dass nicht vorher drauf gekommen, aber du solltest überlegen, ob du immer so einen Dummen findest oder nicht lieber anders vorgehen willst…

Ich bin dankbar für alle, die mir helfen. :slight_smile:

Wenn man nicht möchte, muss man auch nicht, das ist ja auch kein Zwang.

Das war in der Tat der Fehler, den Du da gefunden hast.

Quelle vie de chien. Schockierend. Wieder so doof gewesen.

Vielen Dank für Deine Hilfe.

habe mein Durchsuchen der Methoden noch dem Fehler noch zwei weitere Fehler gefunden. Hat sich also auch für mich nochmal gelohnt.

Vielen lieben Dank

Gruß
Lukas

Was war denn der Fehler? Ist so von außen (für mich zumindest) nicht nachvollziehbar.

Hallöle,

schau mal in den Post #4. Im Code von SlaterB. Die Füllung des Spielerarrays findet erst nach Aufruf des GridLayouts statt. Fatalerweise ist in der Klasse, wo das passiert (Spielfeld) ein Verweis auf die Methoden, die die Spielerkarten dem Spieler zuweist. Der Spieler, der erst danach erstellt wird. :wink:

Ist nicht sehr offensichtlich, ja. Aber für jemanden wie mich, der genau weiß wo es steht, sehr peinlich. :smiley:

Gruß
LUkas

Auch wenn ich mich mit dem Pattern nicht gut auskenne (da ich irgendwie die Abhängigkeiten bis heute nicht wirklich gerallt habe) : M-V-C - Model-View-Controller
Was ich mit sagen will : wenn man erst eine GUI zusammenbaut in der man bereits Objekte verwenden will die aber erst nach dem zusammenbauen der GUI erzeugt werden ist eine NPE quasi “bewusst erzwungen”.

Als Tipp : versuche im Sinne MVC die Logik von der GUI zu trennen und eine Art Abstraktionsschicht (gosh, ich arbeite viel zu viel mit Interfaces) zwischen zu ziehen, so dass die einzelnen Komponenten austauschbar werden. Dann ergibt sich von ganz alleine Code der eine gewisse Reihenfolge “erzwingt” womit man sowas vermeiden kann.