Ich habe einige Probleme mit meiner Vererbungsstruktur für eine Reihe von Wrapper-Klassen mit Generika. Dies ist im Grunde die Struktur:Zurückgeben einer generalisierten Oberklasse
public abstract class SuperWrapper<E extends Super>{
private E wrappedObject;
public E getWrappedObject(){
return wrappedObject;
}
}
public abstract class MiddleWrapper<E extends Middle> extends SuperWrapper<E>{}
public class SubAWrapper extends MiddleWrapper<SubA>{}
public class SubBWrapper extends MiddleWrapper<SubB>{}
Die Klassen, die die gleichen Vererbungsstruktur folgen gewickelt sind, so dass im Grunde:
public class Super{}
public class Middle extends Super{}
public class SubA extends Middle{}
public class SubB extends Middle{}
Diese wickelte Klassen sind nicht von mir und kann nicht geändert werden (was der Grund ist, für die Wrapper-Klassen). Diese Struktur funktioniert sehr gut für die meisten Zwecke, jedoch gibt es einige Probleme, wenn ich möchte, dass eine Methode entweder einen SubAWrapper oder einen SubBWrapper zurückgibt.
Das ist, was ich bisher versucht habe:
public static MiddleWrapper<?> findMiddle(String id){
SubAWrapper a = new SubAWrapper();
SubBWrapper b = new SubBWrapper();
if(returnSubA){
return a;
} else{
return b;
}
}
Dies kompiliert und funktioniert, aber Sonarqube warnt vor der Verwendung einer Wildcard in einem Rückgabetyp und von meinem es ist nicht eine der Warnungen googeln das sollte ignoriert werden.
Ein anderer Weg:
public static <E extends Middle> MiddleWrapper<E> findMiddle(String id){
SubAWrapper a = new SubAWrapper();
SubBWrapper b = new SubBWrapper();
if(returnSubA){
return (MiddleWrapper<E>) a;
} else{
return (MiddleWrapper<E>) b;
}
}
Dies kompiliert und arbeitet mit dem aktuellen Code, aber, so scheint es gefährlich. Wenn der Aufrufer diese Methode wie folgt verwendet: MiddleWrapper<SubA> middle = findMiddle("1");
Kompiliert es gut, aber wenn findMiddle versucht, einen SubBwrapper zurückzugeben, wird es eine ClassCastException zur Laufzeit geben. Was würde ich zwar arbeiten, aber tat, war nicht:
public static MiddleWrapper<Middle> findMiddle(String id){
SubAWrapper a = new SubAWrapper();
SubBWrapper b = new SubBWrapper();
if(returnSubA){
return (MiddleWrapper<Middle>) a; //Compile error here
} else{
return (MiddleWrapper<Middle>) b; //and here
}
}
Also im Grunde meine Frage ist, gibt es einen richtigen Weg, um die Methode findMiddle zu schreiben, die, laufen und folgt Best Practices erarbeitet?
Für zusätzliche Kredit, gibt es eine Möglichkeit, eine Methode schreiben, so dass es eine Liste zurückgibt, die sowohl SubA als auch SubB enthält. So aber ohne den Platzhalter:
public static List<MiddleWrapper<?>> getAllMiddle(){
List<MiddleWrapper<?>> ret = Lists.newArrayList();
ret.add(new SubAWrapper());
ret.add(new SubBWrapper());
return ret;
}
ps. An dieser Stelle verwenden wir immer noch Java 6, aber ein Upgrade auf Java 8 ist für nächstes Jahr geplant, so dass Lösungen, die die Funktionalität von Java 7-8 verwenden, weiterhin nützlich sein werden.
Der Grund ist es schlechte Praxis ein wenig erklärt hier wäre: https://sonarcloud.io/organizations/default/rules#q=wildcard auch auf verschiedene Stackoverflow-Fragen wie https://stackoverflow.com/questions/22815023/generic-wildcard-types-should-not-be-used-in-return-parameters. Kann ich MiddleWrapper > oder MiddleWrapper in Rückgabetypen ist es einfach zu lösen, die Frage ist, ob es ohne es möglich ist. –
MatsT
Um dies zu erreichen, sollte MiddleWrapper instanziierbar sein, ohne generische Angaben zu machen. Indem Sie eine generische Schicht entfernen, könnten Sie. Schließlich wird zwischen generischen Constraints und der einfachen Verwendung der API abgewogen. – davidxxx