2017-07-21 6 views
1

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.

Antwort

1

MiddleWrapper ist eine generische Klasse mit einer Middle Klasse parametriert:

public abstract class MiddleWrapper<E extends Middle> extends SuperWrapper<E>{} 

Wenn Sie eine einzige Methode zur Verfügung stellen möchten, die eine List von MiddleWrapper mit mindestens zwei verschiedene generische Typen für MiddleWrapper gibt, haben Sie keine viele Entscheidungen.

Wenn Sie keine Einschränkung für die Middle-Unterklasse festlegen müssen, die zurückgegeben werden kann.
Die Methode, die Sie in Ordnung dargestellt ist:

public static List<MiddleWrapper<?>> getAllMiddle() { 
    List<MiddleWrapper<?>> ret = Lists.newArrayList(); 
    ret.add(new SubAWrapper()); 
    ret.add(new SubBWrapper()); 
    return ret; 
} 

Sie sagte:

Dies kompiliert und funktioniert, aber Sonarqube warnt vor der Verwendung eines Platzhalter in einem Rückgabetyp und von meinem es googeln nicht eine der Warnungen, die ignoriert werden sollten.

Warum wäre die Verwendung der ? in MiddleWrapper<?> eine schlechte Praxis?
Die Klasse MiddleWrapper ist generisch, und in Ihrer Methode können Sie jede Art von Middle als generischen Typ zurückgeben.
Es ist die Art und Weise für diesen Anwendungsfall zu tun.

Jetzt, wenn Sie eine Einschränkung für die Middle Unterklasse festlegen müssen, die zurückgegeben werden kann.
Zum Beispiel einige Middle Unterklasse, aber nicht einige andere.

Sie könnten eine Zwischenklasse einführen, die Mitte erstreckt, macht SubA und SubB es erweitern und die List mit ihm in der getAllMiddle() Methode geben.

public class ValidMiddle extends Middle {...} 

public class SubA extends ValidMiddle{...} 

public class SubB extends ValidMiddle{...} 

Und die Methode sein sollte:

public static List<MiddleWrapper<? extends ValidMiddle>> getAllMiddleWithMoreSpecificType() { 
    List<MiddleWrapper<? extends ValidMiddle>> ret = Lists.newArrayList(); 
    ret.add(new SubAWrapper()); 
    ret.add(new SubBWrapper()); 
    return ret; 
} 

Beachten Sie, dass auf diese Weise ein wenig ausführlicher für den Kunden ist, dass diese deklarierten Typ manipulieren sollte:

List<MiddleWrapper<? extends ValidMiddle>> listOfWrappers = getAllMiddle(); 
+0

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

+0

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

1

In Beispiel angegeben in "Ein anderer Weg" Absatz ClassCastException kann durch Erzwingen der zurückgegebenen Typ vermieden werden:

public static <E extends Middle> MiddleWrapper<E> findMiddle(String id, Class<E> clazz){ 
     SubAWrapper a = new SubAWrapper(); 
     SubBWrapper b = new SubBWrapper(); 
     if(returnSubA){ 
     //check here if a/b is instance of clazz 
      return (MiddleWrapper<E>) a; 
     } else{ 
      return (MiddleWrapper<E>) b; 
     } 
    } 

Und Methodenaufruf wäre:

MiddleWrapper<SubA> middle = findMiddle("1", SubA.class); 
Verwandte Themen