Ich habe folgendes Interface über das ich Manager verwalte, die jeder für sich eine bestimme Entität (bspw. User) verwalten.
<E> Manager<E> getManagerOf(Class<E> type); //Generischer Zugriff auf Manager
UserManager getUserManager();//Konkreter Zugriff auf UserManager
}
Und folgende Klassen:
//...
}
public class ManagementImpl implements Management{//Implementierung Management
private Map<Class<?>,Manager<?>> managers = new HashMap<>();
public ManagementImpl(){
managers.put(User.class, new UserManager());
}
@Override
public <E> Manager<E> getManagerOf(Class<E> type){
return (Manager<E>) managers.get(type);
}
@Override
public UserManager getUserManager(){
return (UserManager) getManagerOf(User.class);
}
}```
Nun würde ich die Methode `getManagerOf(Class<E> type)` gerne so schreiben, dass ich mir den cast in `getUserManager` sparen kann. Mein Ansatz:
```public <E, M extends Manager<E>> M getManagerOf(Class<E> type){
return (M) managers.get(type);
}```Allerdings erkennt der Compiler das nicht als gültige Implementierung von `<E> Manager<E> getManagerOf(Class<E> type)` an (Es kommt die Fehlermeldung, dass ich getManagerOf noch implementieren muss).
Warum funktioniert das nicht?
Das würde genauso funktionieren, hast du dein Interface entsprechend der neuen Signatur angepasst?
public interface Management
{
<E, M extends Manager<E>> M getManagerOf(Class<E> type); // Generischer Zugriff auf Manager
UserManager getUserManager();// Konkreter Zugriff auf UserManager
}
public class ManagementImpl implements Management // Implementierung Management
{
private Map<Class<?>, Manager<?>> managers = new HashMap<>();
public ManagementImpl()
{
managers.put(User.class, new UserManager());
}
@Override
public <E, M extends Manager<E>> M getManagerOf(Class<E> type)
{
return (M) managers.get(type);
}
@Override
public UserManager getUserManager()
{
return getManagerOf(User.class);
}
}
EDIT:
Wobei ich sagen muss, dass der Cast in der ManagementImpl nicht schlimm ist (solange du das einfügen in die Map durch geeignete Methoden kapselst). Die Lösung würde ich vielleicht sogar bevorzugen, da dadurch der “Genericwust” etwas aufgelöst wird.
[QUOTE=EikeB]Wobei ich sagen muss, dass der Cast in der ManagementImpl nicht schlimm ist (solange du das einfügen in die Map durch geeignete Methoden kapselst). Die Lösung würde ich vielleicht sogar bevorzugen, da dadurch der “Genericwust” etwas aufgelöst wird.[/QUOTE]Hm… auch ein Argument. Allerings könnte ich mir sonst sowohl im Interface als auch in der Implementierung alle expliziten getter sparen, da ich dann gleich UserManager um = management.getManagerOf(User.class) schreiben könnte.
Interessantes Phänomen bei <E, M extends Manager<E>> M getManagerOf(Class<E> type):
final Date d = management.getManagerOf(Date.class);```Das kompiliert! :eek: Kann mir das wer erklären!? Hätte erwartet, dass er mir nen Fehler wirft, da Date kein Manager<?> ist.
Vielleicht bleibe ich dann doch lieber bei `<E> Manager<E> getManagerOf(Class<E> type)` :D
Bound mismatch: The generic method getManagerOf(Class) of type Management2 is not applicable for the arguments (Class).
The inferred type Date&Manager2 is not a valid substitute for the bounded parameter <M extends Manager2>
Test2.java /Test/src/test line 15 Java Problem
hier vollständiges Testprogramm zum Kopieren
public class Test2{
public static void main(String[] args)
{
Management2 management = null;
final Date d = management.getManagerOf(Date.class);
}
}
interface Management2 {
<E, M extends Manager2<E>>M getManagerOf(Class<E> type);
}
class Management2Impl implements Management2 {
private Map<Class<?>, Manager2<?>> managers = new HashMap<Class<?>, Manager2<?>>();
public <E, M extends Manager2<E>>M getManagerOf(Class<E> type)
{
return (M)managers.get(type);
}
}
class Manager2<T> { }
daran hatte ich auch kurz gedacht, aber Ausprobieren eingespart,
mal wieder etwas was mich an Generics überrascht, dazu fällt mir bislang nix ein,
außer einfacheres Beispiel:
public class Test2 {
public static void main(String[] args) {
Date d = getListOf(); // kompiliert, später ClassCastException
System.out.println("d: " + d);
}
public static <M extends List>M getListOf() {
return (M)new ArrayList();
}
}
mit extends ArrayList funktionierend, extends List ohne Wirkung…
List ist ein interface. ArrayList nicht. Mehr fällt mir dazu auch nicht ein. Aber ich hätte bei public static <M extends List>M getListOf() schon erwartet, dass das nach dem Type-Erasure so aussieht: List getListOf(). Sehr mysteriös…
ich habe gestern recht viele Links angeschaut, Suchbegriffe wie return type, WildCard, Bounds,
Generics erklären viele, aber ich habe noch keine Seite gefunden die exakt diesen Fall betrachtet,
vielleicht wirklich noch unbekannt, so unwahrscheinlich das auch klingt
return (M) new ArrayList<E>();
}```
Bei dieser jedoch nicht (wie erwartet: cannot convert from List<Object> to Date)
```public static <E, M extends List<E>> M getListOf() {
return (M) new ArrayList<E>();
}```
Also Eclipse zeigt mir hier (Java7) bei beiden nur eine Warnung.
Allgemein kann man natürlich sagen: Casten kann ALLES kaputtmachen.
Aber in diesem konkreten Fall ist zumindest (einigermaßen) klar, warum es mit “List” funktioniert, aber mit “ArrayList” nicht: Wenn man “List” angibt, wird der Typ zu Date & List inferiert. Und so einen Typen kann es ja geben: class SpecialDate extends Date implements List. Wenn man “ArrayList” angibt, wäre der inferierte Typ Date & ArrayList, aber class SpecialDate extends Date, ArrayList geht eben nicht (allgemein darf eine Typvariable nur upper bounds haben, und nur eine Klasse als upper bound - siehe http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.4 ).
Für eine Erklärung des Verhaltens abhängig vom Wildcard (und warum das jetzt bei Java 8 anders zu sein scheint) müßte man wohl genauer suchen.
innerhalb der Methode ist ja klar, auch beim Aufruf erkannt?, List l = getListOf(); durchgelassen aber vor String s = getListOf(); (anders/ deutlicher) gewarnt?
wobei Eclipse-Warning immer noch Zusatz zu Compiler wäre
letzteres ürigens eine finale Klasse, nichts mit Erweiterung, aber soweit kann natürlich nicht dynamisch geprüft werden
hilfreich ist so ein Verhalten sowieso nicht, es muss hier so naheliegend wie bei Generics eben üblich mit einer ClassCastException zur Laufzeit gerechnet werden,
vom Compiler sehenden Auges hingenommen
bei
List<? extends List> list = new ArrayList();
Date d = list.get(4);
gibts ‚cannot convert from capture#1-of ? extends List to Date‘ statt stiller Hoffnung auf SpecialDate
List<? extends List> list = new ArrayList();
Date d = (SpecialDate)list.get(4);
würde das auch gehen. Es geht ja um die Frage, was bekannt sein kann. Aber Typinferenz ist eben so eine Sache, komplizierter als man denkt (AUCH wenn man diese Tatsache berücksichtigt…)
ganz schön weit hergeholt, anderseits funktioniert
ArrayList d = list.get(4);
genausowenig wie mit Date, also eh kein gutes Vergleichsbeispiel,
dieses Konstrukt versteift sich auf List oder was immer als Basis angegeben ist, die Methode getListOf() nicht