Seite 2 von 2

Verfasst: 13. Dez 2007 18:20
von jlerch
Bei mir macht es leider nach wie vor nicht "klick".

Ich kann ehrlich gesagt nicht mal nachvollziehen wieso ich Number.class nicht übergeben darf. Wenn ich das mache bekomme ich nicht mal einen Laufzeitfehler.

Den Ansatz mit dem Dummyobjekt kann ich nachvollziehen, wie es ohne gehen soll seh ich allerdings nicht.

Verfasst: 13. Dez 2007 19:02
von MuldeR
Überleg doch mal, was passiert wenn du es so machst:
this.elementdata = (T[]) Array.newInstance(Number.class, initialCapacity);

Dann erzeugst du ein Array von Number und castest es auf ein Array von T.
Das T ist ein beliebiger Typ deiner generischen Klasse, aber das "Number.class" ist ja jetzt hardcoded!

Im grunde könntest du ansatt dem Code oben also genau so gut das hier nehmen:
this.elementdata = (T[]) Number[initialCapacity];

Sollte klar sein, wieso/wann da Probleme auftreten können.
Denk vorallem daran, dass der Client deine generische Klasse mit einem beliebigen Typ T instanziieren kann ;)

Verfasst: 13. Dez 2007 19:47
von jlerch
T ist von einem beliebigem Typ der von Number erbt dachte ich.
Wie auch immer, wieso bekomme ich keinen Laufzeitfehler wenn ich meine generische Klasse mit Double instanziiere?

Verfasst: 13. Dez 2007 20:36
von MuldeR
nee, T ist ganz beliebig! Sonst müsstest du ja <T extends Number> fordern.
Das darfst du aber net, da laut Aufgabenstellung "Number" in deiner generischen Klasse nicht mehr auftauchen darf!

Du bekommst Laufzeitfehler, soblad du .toArray() aufrufst und dein "interner" Array vom Typ her nicht mit T übereinstimmt!
Das liegt daran, dass .toArray() ein T[] zurückgeben muss, du aber kein Array anderen Typs auf ein Array von T casten kannst.
Die Probleme treten bei folgendem Code auf und du fängst dir ne "ClassCastExceptions" Exception ein:

Code: Alles auswählen

public T[] toArray() {
  return Arrays.copyOf(this.elementdata, this.size);
} 
Genau aus diesem Grund geht auch nicht das, was sich ansonsten als einfachste Lösung angeboten hätte:
this.elementdata = (T[]) Object[initialCapacity];

Das mit dem Dummy Objekt war nur ne notlösung, weil Array.newInstance() ein Parameter vom Typ Class<T> fordert.
Irgendwie musst du ihn also dazu bringen deinen formalen Paramter T zu schlucken. Einfach "T.class" oder sowas tut aber net!
Forderst du einen Dummy vom Typt T, dann kannst du dir mit dummy.getClass() den passenden Typ holen und an newInstance() übergeben.
Ein Integer Object z.B. gibt dir mit .getClass() ein Class<Integer> zurück, so dass Array.newInstance() ein Integer-Array erzeugt.

Das Dummy Parameter funktioniert zwar, ist aber ziemlich hässlich für den Client.
Jetzt kann man sich überlegen, anstatt des Dummy Objekts einfach direkt das zu übergeben, was man eigentlich braucht ;)

Verfasst: 13. Dez 2007 21:34
von karsten
MuldeR hat geschrieben:nee, T ist ganz beliebig! Sonst müsstest du ja <T extends Number> fordern.
Das darfst du aber net, da laut Aufgabenstellung "Number" in deiner generischen Klasse nicht mehr auftauchen darf!
stimmt, strenggenommen hast du Recht. An dieser Stelle (und nur an dieser) habe ich das aber genau so gemacht, da es ja eigentlich genau das ist, was gewollt ist. Funktionieren würde meine Lösung auch ohne das "extends Number". Hier wäre ein kurzes Statement von offizieller Seite ganz gut.

Verfasst: 13. Dez 2007 21:41
von MuldeR
Wie gesagt, wenn du dein Array mit Array.newInstance() erzeugst und den zu T passenden Typ übergibst, dann tut das 1a.
Und zwar ganz egal, mit was du dein <T> instanziierst :wink:

Du hast dann zwar immer noch einen zusätzlichen Parameter im Konstruktor, aber "schöner" geht das wohl in Java nich...

Verfasst: 13. Dez 2007 23:51
von ujs
karsten hat geschrieben:
MuldeR hat geschrieben:nee, T ist ganz beliebig! Sonst müsstest du ja <T extends Number> fordern.
Das darfst du aber net, da laut Aufgabenstellung "Number" in deiner generischen Klasse nicht mehr auftauchen darf!
stimmt, strenggenommen hast du Recht. An dieser Stelle (und nur an dieser) habe ich das aber genau so gemacht, da es ja eigentlich genau das ist, was gewollt ist. Funktionieren würde meine Lösung auch ohne das "extends Number". Hier wäre ein kurzes Statement von offizieller Seite ganz gut.
Also das mit <T extends Number> dürfen wir bestimmt benutzen, das ist sogar so auf den Folien (sed07-08_ex07-slides.pdf, Folie 65).

Ich frage mich halt wieso wir uns überhaupt mit "generic Arrays" in Java rumschlagen müssen. Ich vermute dass die meisten dabei eher verwirrt werden als dass es zum Verständnis beiträgt.
Generic Types "existieren" leider nur beim Kompilieren und werden zur Laufzeit "vergessen" (Type Erasure). Deswegen kann man auch kein Array vom Typ T erstellen, ohne ein Objekt oder die explizite Klasse von T zu haben. Vielleicht ändert sich das ja mal in einer späteren Java Version...

Verfasst: 14. Dez 2007 05:23
von marcel_b
Hi,
zum "Statement von offizieller Seite":

Die Aufgabenstellung sagt "keine Variablen vom Typ Number" - also kein Number-Array etc. Das ein NumberVector nur mit Numbers arbeitet bietet sich aber namenstechnisch an. Daher ist ein <T extends Number> das was man braucht.

Was die Probleme/Verwirrungen mit generischen Arrays angeht:

Auch wenn das das einzige ist, was zu dieser Aufgabe im Forum diskutiert wird - die Aufgabe besteht nicht nur daraus, wie man ein Array anlegen kann.

Ihr solltet auch sehen, wie ein Parameter T sich auf die Methodensignaturen auswirkt und wie dadurch die Typsicherheit gewährleistet wird.

Darüber hinaus stellt die Signatur der Sort-Methode nochmal einen interessanten Punkt dar.

Also bitte die Aufgabe nicht nur darauf reduzieren :)
Guten Morgen :)