swift - medium - ¿Cómo se implementan los métodos de protocolo que devuelven Selfs covariantes?
swift protocol and delegate example (2)
Como dice, no puedes hacer esto, y por una buena razón. No puedes demostrar que mantendrás tu promesa. Considera esto:
class AnotherSubclass: Class {}
let x = AnotherSubclass().instance
Entonces x
debería ser AnotherSubclass
según su protocolo (eso es Self
). Pero en realidad será Subclass
, que es un tipo completamente diferente. No puedes resolver esta paradoja a menos que la clase sea final
. Esta no es una limitación Swift. Esta limitación existiría en cualquier sistema de tipo correcto porque permite una contradicción de tipo.
Por otro lado, algo que puede hacer es prometer que la instance
devuelve algún tipo consistente en todas las subclases (es decir, la superclase). Lo haces con un tipo asociado:
protocol Protocol {
typealias InstanceType
var instance: InstanceType {get}
}
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
class AnotherSubclass: Class {}
let x = AnotherSubclass().instance
Ahora x
es inequívocamente de tipo Class
. (También sucede que es al azar otra subclase, lo cual es un poco raro, pero eso es lo que dice el código).
Por cierto, todo esto generalmente sugiere que estás usando subclases cuando realmente no deberías. La composición y los protocolos probablemente resolverían mejor este problema en Swift. Pregúntese si existe algún motivo por el cual la Subclass
necesite ser realmente una subclase de la Class
. ¿Podría ser un tipo independiente que se ajuste al mismo protocolo? Todo tipo de problemas desaparecen cuando te deshaces de las subclases y te enfocas en los protocolos.
He estado pensando en esto más, y puede haber una manera de obtener lo que estás buscando. En lugar de decir que todas las subclases implementan la instance
, adjunte la instance
como una extensión. Todavía puede anular eso si desea devolver algo más.
protocol Protocol {
init()
}
class Class: Protocol {
required init() {}
var instance: Class { return Subclass() }
}
extension Protocol {
var instance: Self { return self.dynamicType.init() }
}
class Subclass: Class {}
Esto esquiva el problema de la herencia (no se puede crear el mismo " AnotherClass
devolviendo el tipo incorrecto" de esta manera).
error: protocolo ''Protocolo'' requisito ''instancia'' no puede ser satisfecho por una clase no final (''Clase'') porque usa ''Auto'' en una posición sin parámetro, sin resultado
protocol Protocol {
var instance: Self {get}
}
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
Así es como expresaría lo que quiero, en C #. (C # no tiene, según mi conocimiento, una forma de exigir que el parámetro genérico "Self" sea en realidad el Self que conocemos de Swift, pero funciona lo suficientemente bien como documentación que debería hacerme hacer lo correcto).
interface Protocol<Self> where Self: Protocol<Self> {
Self instance {get;}
}
class Class: Protocol<Class> {
public Class instance {get {return new Subclass();}}
}
class Subclass: Class {}
... cómo podría verse en una versión futura de Swift:
protocol Protocol {
typealias FinalSelf: Protocol where FinalSelf.FinalSelf == FinalSelf
var instance: FinalSelf {get}
}
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
Cómo estoy emulando la parte de eso que es relevante para mi problema:
protocol Protocol: ProtocolInstance {
static var instance: ProtocolInstance {get}
}
protocol ProtocolInstance {}
class Class: Protocol {
static var instance: ProtocolInstance {return Subclass()}
}
class Subclass: Class {}
Y, aquí está lo que creo que es la parte relevante de mi código:
protocol Protocol {
static var 🎁: Self? {get} // an existing instance?
static var 🐥: Self {get} // a new instance
func instanceFunc()
}
extension Protocol {
static func staticFunc() {
(🎁 ?? 🐥).instanceFunc()
}
}
Esto realmente tendría sentido y funcionaría si no quieres regresar a Self
para cada subclase como esta:
protocol Protocol : class {
typealias Sub : Self
var instance: Sub {get}
}
Lo que significa que su protocolo define un typealias que tiene que ser una subclase de sí mismo. El siguiente código simplemente funcionaría:
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
Class().instance // Returns SubClass()
Sin embargo, el código anterior no se compila con el error
error: inheritance from non-protocol, non-class type ''`Self`''
que creo que es un error, porque Self
se declara como un tipo de clase. Sin embargo, puedes hacer que funcione de alguna manera como este:
protocol Protocol : class {
typealias Sub : Class
var instance: Sub {get}
}
pero entonces no tiene mucho del protocolo en sí mismo porque solo la clase en sí misma debería conformarse a él.