2017-09-22 1 views
1

ein Protokoll etwas in diese Richtung gegeben:Implementierung Protokoll statische Methode, die nimmt und Selbst (in swift)

protocol Thing { 
    static func *(lhs: Float, rhs: Self) -> Self 
} 

Wie man erwartet, dass eine Feldklasse zu implementieren?

class ThingBox<T: Thing>: Thing { 
    var thing: T 
    required init(thing: T) { self.thing = thing } 
    static func *(lhs: Float, rhs: Self) -> Self { 
    return Self(thing: lhs * rhs.thing) 
    } 
} 

Der Compiler beklagt, dass Self kann nicht in das Verfahren Typ verwendet werden, jedoch ist die Tatsache, dass ThingBox ableitbaren ist, bedeutet, dass ThingBox<T> Verwendung nicht geeignet ist.

Ist es nicht möglich, diese Klasse zu schreiben, ohne sie zu zwingen, final zu sein?

Antwort

1

Ihre * Implementierung hat ein paar subtile Probleme. Hier ist die Implementierung Sie nach:

static func *(lhs: Float, rhs: ThingBox<T>) -> Self { 
    return self.init(thing: lhs * rhs.thing) 
} 

Zuerst Sie nicht Self als Parametertyp verwenden können. Du musst explizit sein. Self bedeutet "der tatsächliche Typ" und wenn Sie das für eine Unterklasse verwenden könnten, würde dies LSP verletzen. Angenommen, ich habe die Typen Animal und Dog (mit den offensichtlichen Beziehungen). Sagen, dass ich die Funktion schrieb:

class Animal { 
    func f(_ a: Self) { ... } 
} 

mit der Bedeutung, dass Animal.fAnimal nehmen, aber Dog.f nur Dog nehmen aber nicht eine Cat nehmen. So würden Sie folgendes erwarten:

dog.f(otherDog) // this works 
dog.f(cat) // this fails 

Aber das ist die Regeln der Substitution bricht. Was passiert, wenn ich dies schreibe:

let animal: Animal = Dog() 
animal.f(cat) 

Das sollte legal sein, weil Animal.f jeden Animal nehmen, aber die Umsetzung von Dog.f kann eine Katze nicht nehmen. Typenkonflikt Boom. Es ist also nicht legal. (Diese Einschränkung existiert nicht für Rückgabetypen. Ich überlasse das dem Leser als Übung. Versuchen Sie, ein Beispiel wie das obige zu erstellen.

Der zweite Fehler war nur ein Syntaxfehler. Es ist nicht Self(), es ist self.init(). In einer statischen Methode ist self der dynamische Typ (was Sie wollen), und Swift erfordert, dass Sie explizit init aufrufen, wenn Sie auf diese Weise verwendet werden. Das ist nur Syntax, kein tiefes Problem wie das andere.


In Swift gibt es keine Möglichkeit, die Sache zu erben, von der Sie sprechen. Wenn Sie zu überlasten wollen, ist das in Ordnung, aber Sie müssen über die Typen explizit sein:

class OtherBox: ThingBox<Int> { 
    static func *(lhs: Float, rhs: OtherBox) -> Self { 
     return self.init(thing: lhs * rhs.thing) 
    } 
} 

Dies macht genau das, was Sie wollen, aber es muss jedes Kind hinzugefügt werden; Es erbt nicht automatisch mit Kovarianz. Swift hat kein starkes Kovarianzsystem, um es auszudrücken.

Das heißt, wenn Sie anfangen, Generika, Protokolle und Unterklassen auf diese Weise zu vermischen, werden Sie in viele, viele seltsame Fälle geraten, sowohl aufgrund der Mathematik, als auch aufgrund der aktuellen Swift-Einschränkungen.Sie sollten sorgfältig fragen, ob Ihr tatsächlicher Code so viel Parametrisierung benötigt. Die meisten Fälle, in denen ich auf diese Art von Fragen stoße, sind "für den Fall, dass wir es brauchen" überdimensioniert und vereinfacht nur Ihre Typen und konkretisiert alles, was Sie brauchen, um das eigentliche Programm zu lösen, das Sie schreiben möchten. Es ist nicht so, dass es nicht schön wäre, unglaublich generische Algorithmen zu entwickeln, die auf höherklassigen Typen basieren, aber Swift ist einfach nicht die Sprache dafür (und möglicherweise nie; es gibt viele Kosten beim Hinzufügen dieser Funktionen).

+0

Aber dies bewirkt, dass alle Aufrufe von '*' den Ergebnistyp 'ThingBox' haben. Wenn ich '*' verwende, um eine Unterklasse von 'ThingBox' zu multiplizieren (sagen wir' Klasse OtherBox: ThingBox {...} '), ist das Ergebnis eine' ThingBox' und keine 'OtherBox', die den Typen zu widersprechen scheint im Protokoll angegeben. – dented42

Verwandte Themen