Discussion:
Probleme mit Generics
(zu alt für eine Antwort)
Fabian Schwahn
2006-06-14 10:05:55 UTC
Permalink
Hallo,

ich habe ein Problem mit Generics.
Es gibt folgende Klassen:

public abstract class Column<T> {
protected T columnValue;

(...)

public T getValue() {
return columnValue;
}
public void setValue(T value) {
if (value != null) {
columnValue = value;
}
}
}

Außerdem existieren diverse konkrete Unterklassen dieser Klasse, z.B.:
public class ColumnText extends Column<String> { (...) }


public abstract class Table {
private ArrayList<Column> columnList;
(...)

public Column getColumn(String columnName) {
if (columnName == null) {
return null;
}

for (Column c: columnList) {
if (columnName.equals(c.getColumnName())) {
return c;
}
}

// no matching column found
return null;
}

(...)
}

Eine Table besteht also aus einer Liste von Columns.

Wenn ich nun die Methode getColumn() in Table aufrufe erhalte ich
folgende Warnung:
table.getColumn("A_ID").setValue(new Long(1));

"Type safety: The method setValue(Object) belongs to the raw type
Column. References to generic type Column<T> should be parameterized"


Wenn ich nun die parametrisierte Variante mit dem Wildcard ? verwende,
bekomme ich folgenden Error:

"The method setValue(capture-of ?) in the type Column<capture-of ?> is
not applicable for the arguments (Long)"

Vielen Dank schonmal.
Carsten Wanke
2006-06-14 11:03:04 UTC
Permalink
Hallo Fabian,
Post by Fabian Schwahn
ich habe ein Problem mit Generics.
public abstract class Column<T> {
protected T columnValue;
(...)
public T getValue() {
return columnValue;
}
public void setValue(T value) {
if (value != null) {
columnValue = value;
}
}
}
public class ColumnText extends Column<String> { (...) }
public abstract class Table {
private ArrayList<Column> columnList;
(...)
public Column getColumn(String columnName) {
if (columnName == null) {
return null;
}
for (Column c: columnList) {
if (columnName.equals(c.getColumnName())) {
^^^^^^^^^^^^^^^^^
Ich vermute, da soll c.getValue() stehen?
Post by Fabian Schwahn
return c;
}
}
// no matching column found
return null;
}
(...)
}
Eine Table besteht also aus einer Liste von Columns.
Wenn ich nun die Methode getColumn() in Table aufrufe erhalte ich
table.getColumn("A_ID").setValue(new Long(1));
"Type safety: The method setValue(Object) belongs to the raw type
Column. References to generic type Column<T> should be parameterized"
Wenn ich nun die parametrisierte Variante mit dem Wildcard ? verwende,
"The method setValue(capture-of ?) in the type Column<capture-of ?> is
not applicable for the arguments (Long)"
Bei mir wird in den Klassen kein Fehler angezeigt. Wenn ich jedoch zwei
Klassen MyColumn und MyTable ableite und

new MyTable(new MyColumn("Hier")).getColumn("Hier");

aufrufe, dann gibt es den Fehler. Du solltest also beim Instanziieren von
einer Column einen generischen Typ angeben. Wenn es variabel sein soll,
darf's auch Object sein:

new MyTable(new MyColumn<Object>("Hier")).getColumn("Hier")

Fragt sich dann allerdings, wozu es den generischen Typ dann überhaupt noch
gibt...

Gruß, Carsten
Ralf Ullrich
2006-06-14 11:49:53 UTC
Permalink
Hi Fabian,
Post by Fabian Schwahn
private ArrayList<Column> columnList;
Wenn du nun ein Element aus dieser Liste holst, ist es nur noch vom
Raw-Type Column. Was zur Warnung
Post by Fabian Schwahn
"Type safety: The method setValue(Object) belongs to the raw type Column.
References to generic type Column<T> should be parameterized"
führt, wann immer du Instanzen dieses Typs benutzt.

Du müsstest also den Elementtyp deiner ArrayList parametrisieren (und
gleichermaßen alle davon abhängigen Methoden Parameter und Rückgabewerte).

Du hast das ja schon versucht, quasi mit

private ArrayList<Column<?>> columnList;

Was aber zu
Post by Fabian Schwahn
"The method setValue(capture-of ?) in the type Column<capture-of ?> is not
applicable for the arguments (Long)"
führen muss, denn ? steht ja für jeden denkbaren Typ, und dieser muss
nicht assignment compatible zu Long sein, daher der Error.


Relativ einfach wird es, wenn z.B. alle deine Columns Strings enthalten,
dann machst du einfach:

private ArrayList<Column<String>> columnList;

oder

private ArrayList<ColumnText> columnList;


Schwierig wird es, wenn du verschiedene Typen unterstützen möchtest. Deine
ArrayList also z.B. sowohl Column<String> als auch Column<Long> enthalten
können soll. Das kannst du nämlich zunächst mal nur so fassen:

private ArrayList<Column<? extends Object>> columnList;

Damit kannst du jetzt jede Art von Column<T> in die Liste einfügen. Aber
wenn du sie wieder rausholst, sind es eben zunächst nur Column<? extends
Object>, was dann auch der Rückgabetyp deiner getColumn() Methode sein
müsste.

Mit einem Column<? extends Object> kannst du aber nur begrenzt etwas
anfangen. Du kannst z.B. Werte herausholen, allerdings nur als Object:

Column<? extends Object> col = getColumn(...);
Object value = col.getValue();

Du kannst aber keine Werte hineinschreiben, denn ? steht ja wieder für
jeden denkbaren Typ, und der muss wieder nicht assignment compatible zu
Object sein.


Was also tun?

Nun einen Teil der Lösung hast du schon:

Du definierst für jeden Value-Typ, den du benutzt eine explizite Klasse:

class ColumnText extends Column<String> {...}
class ColumnLong extends Column<Long> {...}

Deine ArrayList definierst du wider so, dass sie Columns jeder Art halten
kann:

private ArrayList<Column<? extends Object>> columnList;

Und wenn du nur ein Objekt aus einer Column brauchst, machst du es auch
wie oben:

Column<? extends Object> col = getColumn(...);
Object value = col.getValue();

Brauchst du aber den genauen Value-Typ oder willst du den Spaltenwert
setzen, dann machst du:

ColumnText col = (ColumnText) getColumn(...);
String value = col.getValue();
col.setValue(value);

ColumnLong col = (ColumnLong) getColumn(...);
long value = col.getValue().longValue();
col.setValue(Long.valueOf(value));

Um diesen Cast kommst du nicht herum, er ist aber vertretbar, da du ja
offensichtlich den Value-Typ kennst und somit auch wissen musst, welche
Art von Column du erwartest.


HTH (und ohne Gewähr, bei Generics mach ich auch noch Fehler und ich
müsste eigentlich meine Aussagen erst mal ausprobieren, wozu ich gerade
aber keine Lust habe. Bin aber zuversichtlich es diesmal auch ohne Test
hingebracht zu haben.)

cu
Stefan Ram
2006-06-14 15:31:49 UTC
Permalink
Post by Ralf Ullrich
Schwierig wird es, wenn du verschiedene Typen unterstützen möchtest. Deine
ArrayList also z.B. sowohl Column<String> als auch Column<Long> enthalten
private ArrayList<Column<? extends Object>> columnList;
»String oder Long« kann man noch etwas spezieller formulieren
als »Object«:

public class Main
{
static
< T extends java.lang.Object &
java.io.Serializable &
java.lang.Comparable< T >>
void f( final T t ){}

public static void main( final java.lang.String[] args )
{ f( new java.lang.String( "0" ));
f( new java.lang.Long( 0 )); }}
Stefan Ram
2006-06-14 15:57:38 UTC
Permalink
Post by Stefan Ram
Post by Ralf Ullrich
private ArrayList<Column<? extends Object>> columnList;
»String oder Long« kann man noch etwas spezieller formulieren
Es kann aber sein, daß das in obigem Kontext nicht
angewendet werden kann. In bestimmten Fällen wird
nur ein einfacher Referenztyp akzeptiert.

Ingo R. Homann
2006-06-14 11:49:20 UTC
Permalink
Hi,
Post by Fabian Schwahn
Hallo,
ich habe ein Problem mit Generics.
public abstract class Column<T> {
protected T columnValue;
(...)
public T getValue() {
return columnValue;
}
public void setValue(T value) {
if (value != null) {
columnValue = value;
}
}
}
public class ColumnText extends Column<String> { (...) }
public abstract class Table {
private ArrayList<Column> columnList;
(...)
Eine Table besteht also aus einer Liste von Columns.
So hast Du es implementiert. Sinnvollerweise sollte sie aber aus einer
Liste von Columns<T> bestehen:

public abstract class Table<T> {
private ArrayList<Column<T>> columnList;
(...)

Hth,
Ingo
Lesen Sie weiter auf narkive:
Loading...