2017-11-24 5 views
1

Ich habe Probleme mit Generika Umsetzung Fabrik für einen bestimmten AnwendungsfallFabrik Muster mit Generika in Java

Ich habe Modellklassen:

class BaseModel { } 

class ModelA extends BaseModel { } 

class ModelB extends BaseModel { } 

und entsprechende Dienstleistungen an:

class Service<T extends BaseModel> { } 

class ServiceA extends Service<ModelA> {} 

class ServiceB extends Service<ModelB> {} 

Meine Factory-Klasse, um Service entsprechend dem Modell zu erstellen, das es bedient:

class Factory { 
    private Map<Class<? extends BaseModel>, Class<? extends Service>> registry; 

    Factory(){ 
     registry = new HashMap<>(); 
     registry.put(ModelA.class, ServiceA.class); 
     registry.put(ModelB.class, ServiceB.class); 
    } 

    Service getService(Class<? extends BaseModel> clazz) throws IllegalAccessException, InstantiationException { 
     return registry.get(clazz).newInstance(); 
    } 
} 

Und eine Klasse, die die Fabrik

class Handler<T extends BaseModel>{ 
    private Service<T> myService; 

    Handler(Class<T> modelClass) { 
     Factory fact = new Factory(); 
     try { 
      myService = fact.getService(modelClass); 
     } catch (IllegalAccessException | InstantiationException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

ich verwendet erhalte eine Warnung für die Zeile, die die Fabrik nutzt den Dienst zu bekommen:

"Unchecked assignment 'Service' to 'Service<T>'" 

Ich verstehe, warum ich die Nachricht bekommen, da die getService Methode gibt Service, aber ich brauche Service<T> und verwirrt darüber, wie ich den Code ändern kann Service<T>

Alle sugge zu bekommen Stände?

Danke!

+0

Und was * Rückgabetyp * ist die Methode 'getService()' haben? ist es "Service" oder "Service "? – alfasin

+0

Sie könnten ' Service getService (Klasse clazz)' verwenden und den zurückgegebenen Wert in 'Service 'umwandeln. Sie werden immer noch eine ungeprüfte Warnung in der Methode selbst erhalten, aber das ist eine der Schwächen der Laufzeit-Löschung von Java-Generics. – bcsb1001

+0

Es gibt zwei Probleme: Erstens gibt es keine Möglichkeit, eine Map zu erstellen, bei der verschiedene Schlüssel verschiedenen generischen Typen entsprechen. Es kann einfach nicht gemacht werden. Zweitens gibt es keine Möglichkeit, die Reflektion sicher zu verwenden, um ein generisch typisiertes Objekt zu erzeugen (wie 'ArrayList ' oder 'Service '). Sie wären besser dran, generische Typisierung aus dem Service zu entfernen. Können Sie einige Beispiele für Service-Methoden anzeigen, die den Modell-Subtyp verwenden? – VGR

Antwort

0

Java-Generika und Vererbung interagieren manchmal counter-intuitiv, d. H. Service<ModelA> ist nicht verwandt (in Bezug auf die Vererbung) zu Service<BaseModel>. Sie können jedoch zwei Implementierungen von getService() schreiben und die Dienstkonstruktoren explizit aufrufen (nicht durch Reflektion). Ihr Handler benötigt eine Instanz Ihres Modells, sodass das Java-Override den richtigen Service-Konstruktor für Sie auswählt.

Worüber ich in meiner ursprünglichen Antwort nicht nachgedacht habe: Sie müssen für alle BaseModel Implementierungen in Ihrer Fabrik fangen (überschreiben wird die spezifischste Methode wählen) und auch Ihr Service muss gegen eine Schnittstelle implementiert werden, um die Saiten zusammen.

class BaseModel { } 

class ModelA extends BaseModel { } 

class ModelB extends BaseModel { } 

interface Service { } 

class ServiceImplementation<T extends BaseModel> implements Service { } 

class ServiceFactory { 
    public static Service getService(ModelA model) { return new ServiceImplementation<ModelA>(); } 
    public static Service getService(ModelB model) { return new ServiceImplementation<ModelB>(); } 
    public static Service getService(BaseModel model) { 
     throw new UnsupportedOperationException("Unknown Service Model"); 
    } 
} 

class Handler<T extends BaseModel> { 
    private Service service; 

    Handler(T model) { 
     service = ServiceFactory.getService(model); 
    } 
} 
+0

Ich bin nicht sicher, wie ich das tun kann, während ich den Handler generisch halte. Wenn Sie Ihre Antwort erweitern könnten, wäre es sehr hilfreich :) – bizz

+0

Ich habe gerade meine Antwort bearbeitet, versucht, es einfach zu halten. HTH –

+0

Vielen Dank! Dies gibt mir einen Fehler "Kann Methode 'getService (T)" nicht lösen, was Sie denken, dass ich vermisse? – bizz

0

Hier können Sie die Klasse als T erfassen und eine Instanz davon zurückkehren (mit den Darstellern):

<T extends BaseModel> Service<T> getService(Class<T> clazz) throws IllegalAccessException, InstantiationException { 
    return (Service<T>) registry.get(clazz).newInstance(); 
} 
+0

Vielen Dank für Ihre Antwort! Ich bekomme die ungeprüfte Fall-Warnung in dieser Casting-Zeile – bizz

+0

Ja, newInstance() gibt Objekt zurück, das ist eine unsichere Umwandlung, aber wenn Sie nur T extends BaseModel erhalten und alle Fälle registrieren, werden Sie niemals fehlschlagen –