# String zu Objekt einer eigenen Klasse casten



## Bertold Bricht (28. Apr 2010)

Hallo liebes Forum,

Ich habe mich jetzt hier mal angemeldet, da mir das große Google nicht weiter helfen konnte...

*Folgendes Problem:*
_Ich muss einen String in ein Objekt einer selbst erstellten Klasse casten._

Um mir selbst etwas mehr in Java beizubringen, versuche ich mich nämlich gerade daran, die Grundstruktur eines Spiels zu entwickeln und muss, damit der Inhalt desselben bequem editierbar bleibt (in einem Editor) jetzt die darin enthaltenen Karten, bzw Levels abspeichern. Speichern scheint gut zu funktionieren, hab mir jede Zeile asugeben lassen, alles an Ort und Stelle. Nur beim Auslesen bekomme ich dieses Problem.

Da es bei dem File für die Optionen, wo ich jedoch nur primitive Dateitypen und Strings brauchte,  prima geklappt hat, einen _DataInputStream_, bzw _DataOutputStream _zu benutzen (wo ja einzelne Zeilen als Strings hintereinander abgespeichert werden), wollte ich dasselbe System auch bei den Karten anwenden. Jedoch sind diese zur Strukturierung in Zellen aufgeteilt, also Objekte meiner selbst erstellten Klasse _Cell_. Und nachdem ich alle Zeilen erfolgreich (habe mir wieder alles ausgeben lassen) in eine _Queue _eingelesen habe, muss ich diese nun in die Cell casten - was mir immer wieder Fehler beschert.

Momentan probiere ich es noch so:

```
Object lString = new String(lOptions.front().toString());
iCells[i][j] = (Cell) lString;
lOptions.dequeue();
```
*lOptions *ist bereits initialisiert. Es handelt sich um eine _Queue _aus dem Informatikunterricht, in welche die einzelnen Zeilen der Datei hintereinander eingelesen sind. Ich habe den Inhalt überprüft und ich bin mir sicher, dass der Inhalt richtig ist. _front()_ gibt das nächste Element aus, _dequeue()_ löscht es und macht Platz für das nächste.

*iCells *ist das mehrdimensionale Array vom Typ _Cell _(meine Klasse), in welchem die Zellen gelagert werden.

*i und j* werden dabei von einer verschachtelten _for-Schleife_ durchlaufen. Diese funktioniert auf jeden Fall einwandfrei. Diese Schleifen sorgen auch dafür, dass der oben geschriebene Code mehrmals aufgerufen wird.

Der Fehler den ich erhalte, ist Folgender (durch eine _catch_-Verzweigung erhalten):

```
java.lang.ClassCastException: java.lang.String cannot be cast to Cell
```


----------



## SlaterB (28. Apr 2010)

praktisch niemand muss jemals new String() aufrufen, wenn du sowas siehts, gedanklich gleich streichen,

Object lString = lOptions.front().toString();
macht dasselbe

------

toString() liefert immer einen String, und einen String kannst du nicht casten,
ein String ist und bleibt ein String

vielleicht
Object o = lOptions.front();
iCells_[j] = (Cell) o;_


----------



## Landei (28. Apr 2010)

SlaterB hat gesagt.:


> ... und einen String kannst du nicht casten,
> ein String ist und bleibt ein String



<klugscheiß>Das stimmt nicht ganz, man kann einen String erfolgreich zu Object, Serializable, Comparable<String> und CharSequence casten.</klugscheiß>

Aber das geht nur, weil er das ja (auch) schon ist. Tomaten in Gemüse casten geht, aber Tomaten in Elefanten casten geht nicht.


----------



## Bertold Bricht (29. Apr 2010)

SlaterB hat gesagt.:


> Object lString = lOptions.front().toString();
> macht dasselbe


Da war ich mir noch nicht so ganz sicher - bei meinen Experimenten schien es mir, dass es einen Unterschied macht, zumindest bei den primitiven Datentypen ein Objekt, statt den Datentyp sebst zu erzeugen (also "new Integer(int i)", statt nur "int i")



SlaterB hat gesagt.:


> vielleicht
> Object o = lOptions.front();
> iCells_[j] = (Cell) o;_


_
Hatte ich auch schon mal, selber Fehler.


		Code:In die Zwischenablage kopieren


java.lang.ClassCastException: java.lang.String cannot be cast to Cell

Sicherheitshalber habe ich noch einmal die Methode front() herausgesucht, die gibt auf jeden Fall ein Object zurück:


		Java:In die Zwischenablage kopieren


public Object front()
{	Object item = null;
	if (!this.isEmpty())
	{
		item = this.first.getItem();
	}
	return item;
}




Landei hat gesagt.:



			Tomaten in Gemüse casten geht, aber Tomaten in Elefanten casten geht nicht.
		
Zum Vergrößern anklicken....

Liegt hier vielleicht der Fehler? Wenn ich doch aus einem Object einen String machen kann, warum gibt es dann keine Möglichkeit, diesen Vorgang umzudrehen?_


----------



## SlaterB (29. Apr 2010)

wenn du von einem Auto ein Foto machen kannst, dann ist das Umdrehen nicht ganz so leicht..,
es sei denn du weißt ganz genau wie das Auto aufzubauen ist, das Foto z.B. ein Bauplan ist und du danach in einer Fabrik ein neues Auto zusammensetzt, das wäre halbwegs denkbar,

was steht denn nun genau in dem String, was macht eine 'Cell' aus?
aus einem String "x=4, y=5" könnte man die beiden Ziffern mühevoll extrahieren und einen Konstruktor new Cell(x,y) aufrufen, falls vorhanden,

nur mit den Fingern schnipsen wird aber nichts bewirken


----------



## Landei (29. Apr 2010)

Bertold Bricht hat gesagt.:


> Liegt hier vielleicht der Fehler? Wenn ich doch aus einem Object einen String machen kann, warum gibt es dann keine Möglichkeit, diesen Vorgang umzudrehen?



Du kannst _versuchen_, den Vorgang umzudrehen, also aus dem Gemüse wieder eine Tomate zu machen. Wenn's aber Spinat war, geht das mit einer ClassCastException krachen.

Eigentlich ist es ganz einfach: Eine Instanz irgendwo im Speicher hat immer einen "echten" Typ (z.B. das, was man mit instance.getClass() abfragen kann), aber die Referenzen auf dieses Stückchen Speicher können durchaus einen anderen Typ haben, entweder den einer Oberklasse oder eines implementierten Interfaces. Das tut man, weil der "exakte" Typ in einem bestimmten Kontext oft nicht wichtig ist: Ein TreeSet interessiert vor allem, dass seine Elemente sortierbar sind und nicht, ob es Tomaten oder Elefanten sind.


----------



## Bertold Bricht (29. Apr 2010)

SlaterB hat gesagt.:


> was steht denn nun genau in dem String, was macht eine 'Cell' aus?


Eine Zelle ist bei mir ein dynamischer datentyp, in mehreren ArrayLists werden die Inhalte dieser Zellen abgespeichert. Um jeden Wert aus diesen einzeln in einen File zu schreiben, müsste ich mich bzw mein programm sich ganz schön verrenken - Vor allem, weil ich es soweit übersichtlich halten möchte, dass ich in späteren Versionen möglichst einfach Dinge hinzufügen kann. Wenn ich mit dieser Technik die Klasse Zelle verändern möchte, aus welchem Grund auch immer, dann müsste ich jedesmal den ganzen Speicherscript wieder mühsam überarbeiten.


----------



## Bertold Bricht (29. Apr 2010)

WHOHOO! Es funktioniert!

Ich habe statt einem DataInputStream jetzt einen ObjectInputStream verwendet und den Code generell überarbeitet. Trotzdem danke für die wirklich schnelle Hilfe!


----------



## Noctarius (29. Apr 2010)

Bertold Bricht hat gesagt.:


> WHOHOO! Es funktioniert!
> 
> Ich habe statt einem DataInputStream jetzt einen ObjectInputStream verwendet und den Code generell überarbeitet. Trotzdem danke für die wirklich schnelle Hilfe!



Klingt aber auch nach einer ziemlich gefrickelten Variante.


----------



## Bertold Bricht (29. Apr 2010)

Noctarius hat gesagt.:


> Klingt aber auch nach einer ziemlich gefrickelten Variante.




```
public Map(String pPath)    // Load a map
    {
        iPath = pPath;

        ObjectInputStream in = null;   

        try
        {
            in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(iPath)));  // Open a file stream

            iName = in.readUTF();   // reads a String

            iCellsWidth = in.readInt();     // reads an int
            iCellsHeight = in.readInt();
            System.out.println("Map Size: " + iCellsWidth + ", " + iCellsHeight);   // For check purposes

            iCells = new Cell[iCellsWidth][iCellsHeight];   // The Cell Array is declared

            int lCounter = 1;   // For check purposes, too
            for(int i = 0; i < iCellsWidth; i++)
            {
                for(int j = 0; j < iCellsHeight; j++)
                {
                    Object lObject = in.readObject();       // reads an Object
                    iCells[i][j] = (Cell) lObject;      // casts it back to Cell
                    System.out.println("Step " + lCounter + " (" + i + ", " + j + "): " + iCells[i][j]);
                    lCounter++;
                }
            }

            in.close();

            System.out.println("Map " + iName + " loaded");
        }
        catch (Exception e)
	{
	    System.err.println ("Error reading Map File: " + e.toString());
        }
    }
```
Ich finde, es schaut ganz vernünftig aus^^

Die Speicherung verläuft ähnlich, da war es vor Allem natürlich wichtig darauf zu achten, dass Name, Map-größe und die Zellen in der richtigen Reihenfolge abgerufen werden.


----------



## maki (30. Apr 2010)

"gefrickelt" finde ich sehr diplomatisch wenn ich den Code so sehe, "vernünftig" ist er auf keinen Fall.

Dir ist klar, dass wenn du die Cell Klasse änderst, deine serialisierten Objekte  nicht mehr geladen werden können?

Namenskonventionen gibt es aus guten Gründen, und der lautet nicht dass du deine eigenen erfindest, was zu  Henker soll das Präfix "i" darstellen? 
Dass die Hungarian Notation Schrott ist hat sogar MS mittlerweile eingesehen.

Nicht entmutigen lassen, aber bitte auch keine Ratschläge in den Wind schlagen aus unverständnis


----------



## Noctarius (30. Apr 2010)

Ich würde ja zu einem eigenen (z.B. XML-basiertem) Datenformat raten. Dies kann man beliebig erweitern.

Schreiben kann man es ganz einfach (im schlimmsten Fall durch Concatination) und lesen z.B. sehr simpel mit dem Ding in der Signatur.

Die normale Java Serialisierung als Bytestream hat halt einige Nachteile. Der schlimmste, den maki schon genannt hat: Wenn du deine Cell Klasse mal erweitern willst lassen sich die Daten nicht mehr einlesen (ohne Tricks).


----------



## Bertold Bricht (30. Apr 2010)

maki hat gesagt.:


> was zu  Henker soll das Präfix "i" darstellen?


i wie instance, das soll mir einfach zeigen, dann es sich um eine Instanzvariable handelt - So zumindest habe ich es in der Schule gelernt (bin gerade in der 12. Klasse und dies alles hier geht bereits eine ganze Ecke über das Abi hinaus)



Noctarius hat gesagt.:


> Ich würde ja zu einem eigenen (z.B. XML-basiertem) Datenformat raten. Dies kann man beliebig erweitern.


Ich habe mir ehrlich gesagt keine Gedanken über andere Speichermöglichkeiten gemacht, da in den Tutorials, die ich gefunden habe, lediglich Streams behandelt werden.

Und ist erweitern nicht doch möglich? Wenn mir nach Abruf des Streams Daten fehlen, kann ich die Speicherplätze doch einfach mit Standartwerten belegen?

EDIT: Das letzte nehme ich zurück, habe es grad mal ausprobiert, der Cell Klasse eine weitere Instanzvariable zu geben.


----------



## Noctarius (30. Apr 2010)

Es geht schon, aber eben nur mit Tricks (nämlich einer festen SerialID)


----------

