Java Generics Probleme

Halus

Lt. Junior Grade
Registriert
Juli 2013
Beiträge
373
Hallo,
Ich benötige in letzter Zeit etwas mehr Generics aber ich habe das Gefühl diese nicht korrekt verstanden zu haben.
Zumindest nach dem durchlesen von der Oracle Doc. Zudem komme ich dadurch auch zu fragen von Vererbung :O

1.) Warum muss ich wenn ich einer Methode etwas vom Typ T übergeben möchte auch einen "Rückgabewert" angeben obwohl es nur ein setter ist?

Bsp: <T extends IDate> void setDate(T date);
funktioniert ohne das vorangestellte <> nicht, weshalb wird es benötigt wenn es doch keine Rückgabe hat?
Oder ist das wirklich nur dafür da um, das T in der Signatur genau zu spezifizieren?
Und kann das dann nur ändern wenn ich auch bei der Klasse nen Generic hinzufüge.

2.) Bei einer Map muss man plötzlich ? angeben anstatt T damit es funktioniert.
Bsp.: Map<? extends IKey,? extends ITask> funktioniert, aber wieso auf einmal ? anstatt einfach nem T?

3.) Warum funktioniert das nicht:

Code:
class Fake implements IFake;

class Foo{

<T extends IFake> void setFake(Class<T> claa,T co);
}

init(){
Fake a = new Fake();
Foo.setFake(a.getClass(),a);
}

Wenn ich sowas mache passt der erste Parameter, aber der zweite kann nicht applied werden. Und mir wird vorgeschlagen,dass der zweite Parameter in setFake nach Fake geändert wird. Sehe nicht weshalb das so ist.
 
Hier schon mal zwei Links als Antwort auf 1. + 2. - das sollte verständlicher sein, als wenn ich das formuliere :)

Halus schrieb:
1.) Warum muss ich wenn ich einer Methode etwas vom Typ T übergeben möchte auch einen "Rückgabewert" angeben obwohl es nur ein setter ist?

Bsp: <T extends IDate> void setDate(T date);
funktioniert ohne das vorangestellte <> nicht, weshalb wird es benötigt wenn es doch keine Rückgabe hat?
Oder ist das wirklich nur dafür da um, das T in der Signatur genau zu spezifizieren?
Und kann das dann nur ändern wenn ich auch bei der Klasse nen Generic hinzufüge.
http://openbook.galileocomputing.de...tml#dodtp710f88eb-3589-44a7-9919-b2780446afc0

Halus schrieb:
2.) Bei einer Map muss man plötzlich ? angeben anstatt T damit es funktioniert.
Bsp.: Map<? extends IKey,? extends ITask> funktioniert, aber wieso auf einmal ? anstatt einfach nem T?
http://openbook.galileocomputing.de...tml#dodtp3d148a00-5e2e-421f-8bae-99afc9bf304a
Ergänzung ()

Halus schrieb:
3.) Warum funktioniert das nicht:

Code:
class Fake implements IFake;

class Foo{

<T extends IFake> void setFake(Class<T> claa,T co);
}

init(){
Fake a = new Fake();
Foo.setFake(a.getClass(),a);
}

Wenn ich sowas mache passt der erste Parameter, aber der zweite kann nicht applied werden. Und mir wird vorgeschlagen,dass der zweite Parameter in setFake nach Fake geändert wird. Sehe nicht weshalb das so ist.

Hier die Doku zu Object::getClass(): http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#getClass()

"The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called."

Das macht Sinn, wenn du z.B. darüber nachdenkst, dass deiner "Fake a" Referenz auch eine Instanz zugewiesen werden könnte, die von Fake erbt.

a.getClass() leifert also Class<? extends Foo>.

Der erste Parameter deiner Methode setFake würde als ersten Parameter aber nur Class<Foo> erlauben.

Wenn du deine Signatur in
Code:
 <T extends IFake> void setFake(Class<? extends T> claa,T co)
änderst, sollte der Compiler zufrieden sein.
 
Okay, danke sehr.
Das Buch kenne ich zwar, aber ich werde es mir noch mal genauer durchlesen und ausprogrammieren. Ansonsten frag ich halt nochmal hier nach.

ICh hätte da noch eine Frage.
Macht es überhaupt Sinn den Rückgabetyp bzw Parameter mit Generics anzugeben?
,wenn die Klasse nicht parameterisiert ist.
Wo ist der Unterschied wenn ich:
public <T extends IFoo>T getData();
gegen
public IFoo getData();
habe?

Ja, ich habe das Buch bereits durchgeschaut.
Aber ich sehe jetzt nicht wirklich klar wo da der Unterschied liegt. Ich kann ja bei beiden immer Klassen die von IFoo erben zurückgeben.
 
Als grundsätzliche Regel würde ich Dir ans Herz legen: Verwende niemals Generics, wenn es nicht unbedingt erforderlich ist. Ich habe auch schon Kollegen damit herumspielen und sich gedanklich verirren sehen, wo die Lösung dann am Ende viel einfacher war.

Ein gutes Beispiel sind generische Rückgabeparameter. Das macht z.B. dann Sinn, wenn die Typisierung des Rückgabeparameters von einem Eingabeparameter abhängig ist. Beispiel:

Code:
<T> T getFirstElementOfList(List<T> list) {
  return list.get(0); // return the first element in the given list
}

In Deinem Beispiel hat die Methode keine Eingabeparameter, sondern soll nur einen generischen Rückgabeparameter haben. Das ist äußerst selten, aber es gibt auch dafür einen Anwendungszweck (hatte ich im Projekt selbst auch schon). Und zwar kann man sich so vermeintliche Typsicherheit erzwingen, wenn die Logik in der entsprechenden Methode nicht wissen kann, von welchem Typ der Rückgabewert sein wird, die äußere Logik, welche die Methode aufrufen wird, dies aber aus dem Kontext heraus bestimmen kann. Beispiel:

Code:
class DataProvider {
  <T> T getData(String key) {
    // load data of unknown type using the key
    Object data = ..... ;
    return (T) data; // expicit cast is necessary here and therefore potentially NOT typesafe
  }
}

class DataConsumer {
  void fetchSomeData() {
    DataProvider provider = new DataProvider();
    Integer year = provider.getData("current_year");
    String name = provider.getData("my_name");
  }
}

Das Beispiel geht davon aus, dass es im DataProvider vorkommen kann, dass der zurückzugebende Datentyp je nach übergebenem Key unterschiedlich sein kann. Die getData()-Methode kann daher nur ein Object aus der Datenquelle ermitteln und zurückgeben. Damit man aber nicht bei jedem Aufruf der getData()-Methode einen expliziten Cast auf den gewünschten Datentyp durchführen muss, kann man diesen in die Methode verlagern und einen generischen Typparameter als Rückgabewert definieren. Damit entfällt z.B. in der fetchSomeData()-Methode des DataConsumers der Cast beim Aufruf der getData()-Methode, weil deren Rückgabetyp durch den Typ der lokalen Variablen year und name einmal auf Integer und einmal auf String festgelegt wird. - Das ist eine dreckige Lösung und sollte nach Möglichkeit vermieden werden. Ging aber in unserem Projekt auch nicht anders.

Wenn Du - wie in Deinem Beispiel - ein Interface definiert hast und die Methode lediglich Instanzen unterschiedlicher Implementierungen des Interfaces zurückliefern soll, dann verwende bitte keine Generics. Das Interface als Rückgabeparameter reicht dann vollkommen.
 
Zuletzt bearbeitet:
Zurück
Oben