2016-07-20 16 views
2

Kontext außer Kraft setzen:nicht funktionieren kann mit generischen Parameter

I Klasse A haben, die eine übergeordnete Klasse für Klasse B Klasse A ist auch generische Typen deklariert in der Klassendeklaration hat, die außer Kraft gesetzt werden, indem sie es untergeordneten Klassen ist.

Klasse A hat eine Methode, die einen generischen Parameter benötigt, der nicht Teil der Klassendeklaration ist.

Klasse B überschreibt die Klassendefinition, indem die generischen Typen aus der Klassendefinition angegeben werden und die generische Funktion überschrieben werden soll.

Beispiel:

class A<U: NSDictionary, Z: NSDictionary> : NSObject 
{ 
    func funcToOverride<T : JSONModel>() 
    { 

    } 
} 

class B : A<NSDictionary, NSDictionary> 
{ 
    override func funcToOverride<T:JSONModel>() 
    { 

    } 
} 

Problem:

1:

Klasse B die Methode mit Compiler-Fehler nicht außer Kraft setzen kann

Frage "Method keine Methode aus ihrer Oberklasse überschreibt") Warum tritt das überhaupt auf?

2) Gibt es bekannte Möglichkeiten, dieses Problem zu vermeiden? Meine Oberklasse ist neben B eine Oberklasse für andere untergeordnete Klassen. Daher ist das Hinzufügen eines generischen Parameters in der Klassendefinition problematisch, nur um einen Typ für eine einzelne Funktion zu definieren.

Zusätzliche Hinweise:

Dieser Code ist streng! ein Beispiel aus einem viel komplexeren Code, mit dem ich in einem vorhandenen großen SDK arbeite. Ich muss das Generika-Konfliktproblem spezifisch lösen, indem ich das Hinzufügen von Protokollen oder anderen Klassen, die nicht mit dem aktuellen Kontext zusammenhängen, vermeidet.

Wie falls jemand angefordert hat das Bedürfnis nach einem größeren Kontext

Eine ausgedehntere Probe Version:

class A<ResponseModel : NSDictionary, RedirectModel : NSDictionary>: NSObject { 

    let existingCompletionHandler : (responseModel : ResponseModel?, error : NSError?) -> Void = { 
     (responseModel : ResponseModel?, error : NSError?) -> Void in 
    } 

    func presentWebViewAndWaitForRedirect(redirectQueryPath : String) 
    { 
     let queryPathToDict : [NSObject : AnyObject] = [:] 

     treatRedirectWithResponseParams(queryPathToDict) {[weak self] (model, error) in 
      self?.callSomeRandomRequestWithRedirectModel(model) 
     } 
    } 

    func callSomeRandomRequestWithRedirectModel(redirectModel : RedirectModel?) 
    { 
     let requestResult : [NSObject : AnyObject] = [:] 

     treatRequestResponse(requestResult, completionHandler: existingCompletionHandler) 
    } 

    func treatRedirectWithResponseParams(responseParams : [NSObject : AnyObject], completionHandler : (model : RedirectModel?, error : NSError?) -> Void) 
    { 
     funcToOverride(responseParams, completionHandler: completionHandler) 
    } 

    func treatRequestResponse(someRequestResult : [NSObject : AnyObject], completionHandler : (model : ResponseModel?, error : NSError?) -> Void) 
    { 
     funcToOverride(someRequestResult, completionHandler: completionHandler) 
    } 

    func funcToOverride<T : NSDictionary>(params : [NSObject : AnyObject], completionHandler : (model : T?, error : NSError?) -> Void) 
    { 

    } 
} 


class B: A<NSDictionary, NSDictionary> { 

     //wants to provide something additional or different to the base implementation but still respect the base class logic flow 
     override func funcToOverride<T : NSDictionary>(params: [NSObject : AnyObject], completionHandler: (model: T?, error: NSError?) -> Void) { 

     } 
    } 

class SomeOtherRandomClass : NSDictionary 
{ 

} 

class C: A<NSDictionary, NSDictionary> { 

    func someOtherClassCRequest() 
    { 
     let response : [NSObject : AnyObject] = [:] 

     funcToOverride(response) { (model : SomeOtherRandomClass?, error : NSError?) in 

     } 
    } 
} 
+0

Wie hier gezeigt, macht der Code wenig Sinn. Ihre Frage enthält einige Informationen Was ist 'JSONModel'? Welche anderen Typen entsprechen oder erben von 'JSONModel'? Was ist der Zweck von "T"? Es wird nicht als Funktionsparameter übergeben, also wie wird es verwendet? –

+0

Die Probe soll das Problem zeigen. Sie können beliebige Klassen ersetzen, wenn sie der Logik im Beispiel folgen. Irgendwo "da" wird der Typ T von anderen Funktionen bestimmt, die diesen generischen bezeichnen. Und das T ist generisch, weil es ungefähr 3 verschiedene Typen hat, auf die es abhängig von der aufgerufenen Funktion "konvertiert" wird. Wie auch immer, andere Typen, die von JSONModel oder anderen Funktionen, die diese Funktion aufrufen, erben, sind für das angegebene Problem nicht relevant. – Fawkes

Antwort

1

Vorausgesetzt, dass Sie tatsächlich irgendwo Typ T verwenden, obwohl der Code Beispiel zeigt dies nicht an.

Sie müssen keine Generika verwenden.

In einem Anwendungsfall wird der Eingabetyp T immer als JSONModel definiert. Daher kann sich die Funktion niemals spezialisieren, um auf zusätzliche Eigenschaften oder Methoden zuzugreifen, mit Ausnahme der in JSONModel definierten. Alle Typen, auf die Sie verweisen, müssen JSONModel entsprechen (entweder durch Vererbung oder als Protokoll). Sie können auch einfach JSONModel verwenden.

Der einzige andere Anwendungsfall ist, dass Sie auf bestimmte Mitglieder einer Instanz von JSONModel zugreifen müssen. In diesem Fall können Sie das Überladen von Funktionen verwenden.

Beispiel:

protocol JSONModel { 
    func hello() -> String 
} 

class ModelA: JSONModel { 
    func hello() -> String { 
     return "Hello Model A" 
    } 
} 

class ModelB: JSONModel { 
    func hello() -> String { 
     return "Hello Model B" 
    } 

    func goodbye() -> String { 
     return "Goodbye Model B" 
    } 
} 

class A<U: NSDictionary, Z: NSDictionary>: NSObject 
{ 
    func fromJSON(t: JSONModel) 
    { 
     print("From A: \(t.hello())") 
    } 
} 

class B : A<NSDictionary, NSDictionary> 
{ 
    override func fromJSON(t: JSONModel) 
    { 
     print("From B: \(t.hello())") 
    } 

    func fromJSON(t: ModelB) 
    { 
     print("From B: \(t.goodbye())") 
    } 
} 

let a = A() 
a.fromJSON(ModelA()) // "From A: Hello A" 

let b = B() 
b.fromJSON(ModelB() as JSONModel) // "From B: Hello B" 
b.fromJSON(ModelB()) // "From B: Goodbye B" 
+0

Ich bot nur die einfachste Darstellung meines Codes an. In der Praxis muss ich speziell den Typ T haben, um ihn auf die korrekte Instanz zu serialisieren. Die Funktionen, die nicht im Beispiel gezeigt werden, verwenden diese generische Funktion und übergeben einen bestimmten Typ, wodurch bestimmt wird, wie die Instanz serialisiert wird. Wie auch immer, das ist eine lange Geschichte. Die Idee ist, ich brauche den Code in der oben angegebenen Weise und ich suche nach der Lösung dieses generischen Konflikts, indem ich keine anderen Protokolle/Klassen hinzufüge, da dies ein riesiges SDK ist und Änderungen wie diese auch einige andere "Architektur" -Änderungen erfordern. – Fawkes

+0

Dennoch ist dies eine interessante Idee für den Fall, dass smb die Flexibilität eines einfacheren Codes und die Fähigkeit hat, beträchtliche Änderungen hinzuzufügen. – Fawkes

+0

Bitte klären Sie Ihre Frage, um zu zeigen, wie Sie 'T' verwenden oder warum es als generisch für die Funktion definiert ist. –

1

Sie können nur die override Schlüsselwort weglassen. Die Vererbung funktioniert normal.

import Cocoa 
import XCPlayground## Heading ## 

class JSONModel { 
    required init() { 
    } 
} 

class ModelA: JSONModel { 
    var name: String? 
    required init() { 
    } 
} 

class ModelB: JSONModel { 
    var size: Int? 
    required init() { 
    } 
} 

class A<U: NSDictionary, Z: NSDictionary>: NSObject 
{ 
    let prefix = "A" 

    func funcToOverride<T: JSONModel>(u: [String: AnyObject], c: (T?) -> Void) 
    { 
     let t = T() 
     (t as? ModelA)?.name = "From A: " + ((u["name"] as? String) ?? "-none-") 
     (t as? ModelB)?.size = u["size"] as? Int ?? 0 
     c(t) 
    } 
} 

class B : A<NSDictionary, NSDictionary> 
{ 
    let suffix = "B" 

    func funcToOverride<T: JSONModel>(u: [String: AnyObject], c: (T?) -> Void) 
    { 
     let t = T() 
     (t as? ModelA)?.name = "From B: " + ((u["name"] as? String) ?? "-none-") 

     if t is ModelB { 
      return super.funcToOverride(u, c: c) 
     } 

     c(t) 
    } 
} 

let a = A() 

a.funcToOverride(["name": "foo"]) { (m: ModelA?) in 
    print("From A: \(m) \(m?.name)") 
} 

a.funcToOverride(["size": 10]) { (m: ModelB?) in 
    print("From A: \(m) \(m?.size)") 
} 


let b = B() 

b.funcToOverride(["name": "bar"]) { (m: ModelA?) in 
    print("From B: \(m) \(m?.name)") 
} 

b.funcToOverride(["size": 10]) { (m: ModelB?) in 
    print("From B: \(m) \(m?.size)") 
} 

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true 
+0

Mit dem in meinem Beispiel bereitgestellten Code habe ich einfach die Methode ohne Überschreibung [in der B-Klasse] verwendet und dort einen Ausdruck erstellt. Dann wurde eine Instanz von B erstellt und treatRequestResponse darauf aufgerufen. Die Methode aus B wurde nicht aufgerufen, stattdessen wurde die Methode aus A aufgerufen. – Fawkes