Ex 7.4 Generics

ArVee
Neuling
Neuling
Beiträge: 7
Registriert: 11. Nov 2007 19:13

Ex 7.4 Generics

Beitrag von ArVee »

Hallo,

die NumberVector Klasse auf Generics umzustellen sah nicht so schwer aus, doch jetzt bekomme ich im Testcase ClassCastExceptions, die gar nicht auftreten dürften ;)

Aus dem Testcase:

Code: Alles auswählen

final NumberVector<Number> v = new NumberVector<Number>(12);
v.add(1);
v.add(2);
assertEquals(2, v.size());

final Number[] array = v.toArray();
[...]
Die Zeile mit v.toArray wirft die Exception.

Aber v.toArray sollte T[] zurückliefern:

Code: Alles auswählen

public T[] toArray() {
    return Arrays.copyOf(this.elementdata, this.size);
}
elementdata ist ein Array vom Typ T[] und wird mit einem Trick erstellt, den ich unter Generic Arrays gefunden hatte.

Kann mir da jemand weiterhelfen?

Oder soll man beim NumberVector intern kein Array mehr verwenden?

es11
Neuling
Neuling
Beiträge: 7
Registriert: 7. Jan 2006 21:44
Kontaktdaten:

Beitrag von es11 »

Hi,
ich hab mich auch gefragt ob wir das überhaupt mit Arrays machen sollen, weil dieses workaround mit dem generic Array ist schon etwas komisch :)

Aber hab dann mal in die jdk geschaut, wie die selbst Vector<E> gemacht haben:

Vector.java
version 1.106, 06/16/06

Code: Alles auswählen

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{

.
.
    
protected Object[] elementData;

public Vector(int initialCapacity, int capacityIncrement) {
	super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);

	this.elementData = new Object[initialCapacity];
	this.capacityIncrement = capacityIncrement;
}

public synchronized E get(int index) {
	if (index >= elementCount)
	    throw new ArrayIndexOutOfBoundsException(index);

	return (E)elementData[index];
}
}


Also scheinst du mit dem Array auf dem richtigen weg zu sein ;)

Benutzeravatar
IchWarsNicht
Neuling
Neuling
Beiträge: 10
Registriert: 23. Okt 2007 15:03

Beitrag von IchWarsNicht »

Ok -.-

Ich geb mal nen gute Tip weiter...

Code: Alles auswählen

T[] elementdata = (T[]) Array.newInstance(Number.class, initialCapacity);

marcel_b
Nerd
Nerd
Beiträge: 600
Registriert: 31. Okt 2006 17:04
Kontaktdaten:

Beitrag von marcel_b »

Hi,

die Erzeugung von Arrays mit der oben beschriebenen Lösung bringt Laufzeitfehler (selbst mit Number.class).

Damit das nicht in allzuviel Arbeit ausartet:Es gibt keine saubere Lösung um generische Arrays zu erzeugen.

Wenn dem so wäre, gäbe es vermutlich nicht die (nicht generische) Methode
Object[] List.toArray(), die ein Object[] zurückliefert - vollkommen egal ob die Liste parametrisiert ist oder nicht...

Zumindest könnt ihr jedoch innerhalb eines gewissen Rahmens gewährleisten, dass alles so laufen wird, wie man es erwartet. Zieht in Betracht die Signature einer Methode oder des Constructors zu ändern und schaut euch die Methoden der Klasse java.lang.reflect.Array an (wie oben im Tip beschrieben; nur Number.class ist nicht ausreichend).

es11
Neuling
Neuling
Beiträge: 7
Registriert: 7. Jan 2006 21:44
Kontaktdaten:

Beitrag von es11 »

Schade das es keine saubere Lösung gibt.

Code: Alles auswählen

    public Vector(XXXXXX, final int initialCapacity) {
        if (initialCapacity < 0) {
            throw new IllegalArgumentException("<0");
        }
        this.elementdata = (T[]) Array.newInstance(XXXXXXXX, initialCapacity); 
    }
Habt ihr irgendwie an sowas gedacht? Oder ist das schon zu unsauber? (funktioniert tut es)

// EDIT Marcel: Das war ein bisschen viel Lösung... Das muss als Hilfestellung reichen, oder?

marcel_b
Nerd
Nerd
Beiträge: 600
Registriert: 31. Okt 2006 17:04
Kontaktdaten:

Beitrag von marcel_b »

ja, an sowas hätte ich jetzt auch gedacht... Zumindest ist es das eleganteste, das mir zu diesem Problem einfällt.

Aber das muss als Hilfestellung reichen, oder?

Benutzeravatar
Ultr1
Mausschubser
Mausschubser
Beiträge: 99
Registriert: 13. Okt 2004 17:53
Wohnort: Dreieich
Kontaktdaten:

Beitrag von Ultr1 »

Die Methoden sollen den Clients ja den Subtyp von Number zurückliefern, so dass der Client nicht jedes Ergebnis dieser Methoden von Number wieder runtercasten muss.
marcel_b hat geschrieben: Es gibt keine saubere Lösung um generische Arrays zu erzeugen.
Ich nehme nun mal an, dass also das Array elementsdata weiterhin vom Supertyp Number sein soll. Das würde aber implizieren, dass zb bei einer getter-Methode für ein Array Element nun von einer Number zum generischen Subtyp T gecastet werden muss. Diesen Subtyp verlangt ja der Client.
Jetzt habe ich hier natürlich einen Typcast, bei dem mich schon Eclipse warnt:
(T) NumberVector.this.elementdata[this.pos++];
Type safety: Unchecked cast from number to T.
Da T jedoch laut generischer Signatur ein Subtyp sein muss sollte dieser Cast immer Klappen, sofern der Anwender auch einen Subtyp von Number übergibt.

Meine Frage ist jetzt: Sollen wir hier eine Ausnahmebehandlung für falsche Parametertypen machen (zb bei der add Methode abfangen, dass der Benutzer keinen falschen Typ addet)? Oder fehlt mir einfach eine Idee um dann Runtime Errors zu vermeiden? Lässt man jedenfalls das Array ungenrisch, so taucht dieses Problem definitiv auf.
Es ist nicht entscheidend, was der Mensch tut, sondern was er ist. (Henry Miller)

marcel_b
Nerd
Nerd
Beiträge: 600
Registriert: 31. Okt 2006 17:04
Kontaktdaten:

Beitrag von marcel_b »

>>
Ich nehme nun mal an, dass also das Array elementsdata weiterhin vom Supertyp Number sein soll
<<

Nein. Wie in der Aufgabestellung und in der Übung gesagt, soll eure Lösung ohne Objekte/Arrays vom Typ Number auskommen. Abgesehen davon wirft die toArray() Methode dann eine ClassCastException.

Falsche Parameter kann es nicht geben, da alle Methoden mit dem generischen Parameter T parametrisiert sind.

Evgeni
Windoof-User
Windoof-User
Beiträge: 39
Registriert: 1. Dez 2004 19:22

Beitrag von Evgeni »

marcel_b hat geschrieben:>>
Ich nehme nun mal an, dass also das Array elementsdata weiterhin vom Supertyp Number sein soll
<<

Nein. Wie in der Aufgabestellung und in der Übung gesagt, soll eure Lösung ohne Objekte/Arrays vom Typ Number auskommen. Abgesehen davon wirft die toArray() Methode dann eine ClassCastException.

Falsche Parameter kann es nicht geben, da alle Methoden mit dem generischen Parameter T parametrisiert sind.
Reicht es wenn man in Array Elemente eines Typs hinzufügen kann, oder sollte auch sowas gehen:
X.add(1);
X.add(1.0d);
....

marcel_b
Nerd
Nerd
Beiträge: 600
Registriert: 31. Okt 2006 17:04
Kontaktdaten:

Beitrag von marcel_b »

Letzteres. Alles was in den generischen Paramter T reinpasst - Wenn T= Number, dann Float, Double, Integer usw. Wenn T = Integer, dann nur Integer oder Subtypen davon (wenn Integer welche hätte).

MuldeR
BASIC-Programmierer
BASIC-Programmierer
Beiträge: 131
Registriert: 18. Okt 2005 16:14
Wohnort: (d)armstadt
Kontaktdaten:

Beitrag von MuldeR »

Hallo,

wir scheitern leider die ganze Zeit an dieser Zeile:
this.elementdata = (T[]) Array.newInstance(XXXXXXXX, initialCapacity);

mit folgender Zeile tut es natürlich, solange man <T> mit Number instanziiert:
this.elementdata = (T[]) Array.newInstance(Number.class, initialCapacity);

aber dann könnte man genau so gut gleich das hier machen:
this.elementdata = (T[]) Number[initialCapacity];

Irgendwie muss man es also in der Art hin bekommen, damit es generisch wird:
this.elementdata = (T[]) Array.newInstance(T.class, initialCapacity);

leider geht das so nicht und durch rumprobieren kommen wir hier leider auch nicht weiter :?

Hat jemand noch en Tipp, wie man das hinbekommt und on man es überhaupt mit dem newInstance() hinbekommen kann...

marcel_b
Nerd
Nerd
Beiträge: 600
Registriert: 31. Okt 2006 17:04
Kontaktdaten:

Beitrag von marcel_b »

ja, man bekommt es über newInstance() hin.

Schaut euch die Klasse java.lang.Class an. Auch Class kann parametrisiert werden: Class<T>

MuldeR
BASIC-Programmierer
BASIC-Programmierer
Beiträge: 131
Registriert: 18. Okt 2005 16:14
Wohnort: (d)armstadt
Kontaktdaten:

Beitrag von MuldeR »

marcel_b hat geschrieben:ja, man bekommt es über newInstance() hin.

Schaut euch die Klasse java.lang.Class an. Auch Class kann parametrisiert werden: Class<T>
Okay, soweit sind wir ja jetzt.
Die frage ist, wie ich meinem formalen Parameter T ein Class<T> entlocken kann, um es dem newInstance zu übergeben...

Wir haben das jetzt nach ewig langem rumprobieren so gemacht:

Code: Alles auswählen

public class GenericVector<T> implements Iterable<T> {

  ...

  @SuppressWarnings("unchecked")
  public GenericVector(final T dummyParam, final int initialCapacity) {

    ...

    this.elementdata = (T[]) Array.newInstance(dummyParam.getClass(), initialCapacity);
  }

  ...

}

Code: Alles auswählen

  @Test
  public void toArray() throws Exception {
    final GenericVector<Number> v = new GenericVector<Number>(new Integer(0), 12);

    ...
  
  }
Ist das so im Rahmen der angedachten Lösung? Immerhin scheint es zu funktionieren ......

marcel_b
Nerd
Nerd
Beiträge: 600
Registriert: 31. Okt 2006 17:04
Kontaktdaten:

Beitrag von marcel_b »

Fast. Es braucht jedoch kein Dummyobjekt. Wieso willst du dummyParam ein Class<T> entlocken, wenn du Class<T> übergeben kannst...?

MuldeR
BASIC-Programmierer
BASIC-Programmierer
Beiträge: 131
Registriert: 18. Okt 2005 16:14
Wohnort: (d)armstadt
Kontaktdaten:

Beitrag von MuldeR »

marcel_b hat geschrieben:Fast. Es braucht jedoch kein Dummyobjekt. Wieso willst du dummyParam ein Class<T> entlocken, wenn du Class<T> übergeben kannst...?
Okay, immerhin :)

Beim meinem Dummy-Object vom Type T kann ich halt sicher sein, dass es wirklich vom Typ T ist.
Wenn ich ein (beliebiges) Class<T> Object übergeben lasse, könnte mir der Client doch einfach einen falschen Class-Typ unterjubeln.....


// EDIT

Okay, hab gerade gemerkt, dass man es doch hinbekommen kann. Und zwar so, dass der Typ stimmt ^^

Antworten

Zurück zu „Archiv“