No se admite el uso de algún protocolo como un tipo concreto conforme a otro protocolo


Estoy tratando de mezclar genéricos con protocolos y estoy recibiendo un tiempo muy difícil XD

Tengo cierta arquitectura implementada en un proyecto Android/Java, y estoy tratando de reescribirla para que se ajuste a un proyecto swift/iOS. Pero he encontrado esta limitación.

ProtocolA

protocol ProtocolA {

}

ProtocolB

protocol ProtocolB : ProtocolA {

}

ImplementProtocolA

class ImplementProtocolA <P : ProtocolA> {

    let currentProtocol : P

    init(currentProtocol : P) {
        self.currentProtocol = currentProtocol
    }

}

ImplementProtocolB

class ImplementProtocolB : ImplementProtocolA<ProtocolB> {

}

Entonces, cuando intento establecer el ProtocolB como el tipo concreto que implementa ProtocolA , me sale este error:

No se admite el uso de 'ProtocolB' como un tipo concreto conforme al protocolo 'ProtocolA'

1 ¿hay alguna razón para esta "limitación"?

2 ¿Hay alguna solución para implementar esto?

3 ¿Será soportado en algún momento?

--ACTUALIZADO -

Otra variante del mismo problema, creo:

Ver protocolos

protocol View {

}

protocol GetUserView : View {
    func showProgress()
    func hideProgress()
    func showError(message:String)
    func showUser(userDemo:UserDemo)
}

Protocolos del presentador

protocol Presenter {
    typealias V : View
}

class UserDemoPresenter : Presenter {
    typealias V = GetUserView
}

Error:

UserDemoPresenter.swift Posiblemente pretendía coincidir con ' V ' (también conocido como "GetUserView") no se ajusta a "View"

¿Qué es eso?? Se cumple!

Incluso si utilizo View en lugar de GetUserView, no compila.

class UserDemoPresenter : Presenter {
    typealias V = View
}

UserDemoPresenter.swift Posiblemente la coincidencia prevista ' V '(también conocida como 'View') hace no se ajusta a 'View'

XxDD No lo entiendo, realmente.

--ACTUALIZADO -

Con la solución propuesta por Rob Napier el problema no se arregla, sino que se retrasa.

Cuando intento definir una referencia a UserDemoPresenter, necesito especificar el tipo genérico, por lo que obtengo el mismo error:

private var presenter : UserDemoPresenter<GetUserView>

Usando 'GetUserView' como un tipo concreto conforme al protocolo 'GetUserView' no es compatible

Author: Víctor Albertos, 2015-11-03

1 answers

La razón subyacente de la limitación es que Swift no tiene metatipos de primera clase. El ejemplo más simple es que esto no funciona:

func isEmpty(xs: Array) -> Bool {
    return xs.count == 0
}

En teoría, este código podría funcionar, y si lo hiciera habría muchos otros tipos que podría hacer (como Functor y Mónada, que realmente no se pueden expresar en Swift hoy en día). Pero no puedes. Tienes que ayudar a Swift a clavar esto a un tipo concreto. A menudo hacemos eso con genéricos:

func isEmpty<T>(xs: [T]) -> Bool {
    return xs.count == 0
}

Observe que T es totalmente redundante aquí. No hay razón por la que deba expresarlo; nunca se usa. Pero Swift lo requiere para convertir lo abstracto Array en lo concreto [T]. Lo mismo es cierto en su caso.

Este es un tipo concreto (bueno, es un tipo abstracto que será convertido en un tipo concreto, cualquier momento es instanciado y P se llena):

class ImplementProtocolA<P : ProtocolA>

Este es un tipo totalmente abstracto que Swift no tiene ninguna regla para convertir en un tipo concreto:{[14]]}

class ImplementProtocolB : ImplementProtocolA<ProtocolB>

Necesitas que sea concreto. Esto compilará:

class ImplementProtocolB<T: ProtocolB> : ImplementProtocolA<T> {}

Y también:

class UserDemoPresenter<T: GetUserView> : Presenter {
    typealias V = T
}

Solo porque es probable que te encuentres con el problema más tarde: tu vida será mucho más fácil si haces estas estructuras o final clases. Los protocolos de mezcla, los genéricos y el polimorfismo de clase están llenos de bordes muy afilados. A veces tienes suerte y no se compila. A veces llamará a cosas que no esperas.

Puede que te interese Un poco de respeto por cualquier consecuencia que detalla algunas cuestiones conexas.


private var presenter : UserDemoPresenter<GetUserView>

Este sigue siendo un tipo abstracto. Quieres decir:

final class Something<T: GetUserView> {
    private var presenter: UserDemoPresenter<T>
}

Si eso crea un problema, necesitará crear un cuadro. Ver ¿El protocolo no se ajusta a sí mismo? para la discusión de cómo escribir-borrar para que pueda mantener los tipos abstractos. Pero necesitas trabajar en tipos concretos. No puedes especializarte en un protocolo. Eventualmente debes especializarte en algo concreto en la mayoría de los casos.

 49
Author: Rob Napier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:23