2015-11-03 19 views
39

sto cercando di mescolare farmaci generici con i protocolli e sto ottenendo un xD momento davvero difficileUtilizzando un protocollo come un tipo concreto conforme ad un altro protocollo non è supportato

Ho certa architettura implementata in Android/Java progetto, e sto cercando di riscriverlo per adattarlo a un progetto rapido/iOS. Ma ho trovato questa limitazione.

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> { 

} 

Così, quando si tenta di impostare ProtocolB come il tipo di cemento che implementa ProtocolA, ottengo questo errore:

Usando 'ProtocolB' come un tipo calcestruzzo conforme al protocollo "Protocollo" non è supportato

1 C'è qualche motivo per questa "limitazione"?

2 Esiste qualche soluzione alternativa per ottenere questo risultato?

3 Sarà supportato a un certo punto?

--UPDATED--

Un'altra variante dello stesso problema, penso:

View protocolli

protocol View { 

} 

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

protocolli Presenter

protocol Presenter { 
    typealias V : View 
} 

class UserDemoPresenter : Presenter { 
    typealias V = GetUserView 
} 

Errore:

UserDemoPresenter.swift Possibly intended match 'V' (aka 'GetUserView') does not conform to 'View’

Che cosa è questo ?? È conforme!

Anche se utilizzo View anziché GetUserView, non viene compilato.

class UserDemoPresenter : Presenter { 
    typealias V = View 
} 

UserDemoPresenter.swift Possibly intended match 'V' (aka 'View') does not conform to 'View'

xxDD Non capisco, davvero.

--UPDATED--

Con la soluzione proposta da Rob Napier il problema non viene risolto, invece, è solo ritardato.

Quando una prova per definire un riferimento UserDemoPresenter, devo specificare il tipo generico, in modo da ottenere lo stesso errore:

private var presenter : UserDemoPresenter<GetUserView> 

Using 'GetUserView' as a concrete type conforming to protocol 'GetUserView' is not supported

+2

Anche qui osservato: http: // stackoverflow.com/domande/33112559/protocollo-doesnt-conforme a se stessa. –

risposta

41

La ragione per la limitazione è che Swift doesn' t avere metatipi di prima classe L'esempio più semplice è che questo non funziona:

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

In teoria, questo codice potrebbe funzionare, e se così fosse ci sarebbe un sacco di altri tipi ho potuto fare (come Functor e Monade, che in realtà può essere espresso in Swift oggi). Ma non puoi. Devi aiutare Swift a inchiodarlo a un tipo concreto. Spesso lo facciamo con i generici:

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

noti che T è totalmente ridondante qui. Non c'è motivo per cui dovrei esprimerlo; non è mai usato Ma Swift lo richiede in modo che possa trasformare l'abstract Array nel cemento [T]. Lo stesso è vero nel tuo caso.

Si tratta di un tipo concreto (beh, è ​​un tipo astratto che verrà trasformato in un tipo concreto ogni volta che viene creata un'istanza e P è riempito):

class ImplementProtocolA<P : ProtocolA> 

Questo è un tipo completamente astratta che Swift non ha alcuna regola da trasformare in un tipo concreto:

class ImplementProtocolB : ImplementProtocolA<ProtocolB> 

È necessario renderlo concreto. Questo compilerà:

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

E anche:

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

Solo perché è molto probabile imbattersi in questione in seguito: la vostra vita andrà molto più facile se farete queste struct o classi final . I protocolli di miscelazione, i generici e il polimorfismo di classe sono pieni di bordi molto nitidi. A volte sei fortunato e non verrà compilato. A volte chiamerà cose che non ti aspetti.

Potresti essere interessato a A Little Respect for AnySequence che descrive alcuni problemi correlati.


private var presenter : UserDemoPresenter<GetUserView> 

Questo è ancora un tipo astratto. Intendi:

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

Se ciò crea un problema, è necessario creare una casella. Vedere Protocol doesn't conform to itself? per informazioni su come cancellare il testo in modo da poter contenere tipi astratti. Ma devi lavorare su tipi concreti. Alla fine non è possibile specializzarsi su un protocollo. Alla fine devi specializzarti in qualcosa di concreto nella maggior parte dei casi.

+0

Grazie mille per la soluzione e la spiegazione. Potresti fornire un esempio di codice usando le structs anziché la classe, per comprendere appieno ciò che hai suggerito? –

+1

Intendo solo sostituire la parola 'class' con la parola' struct' nel codice. Se si utilizza 'struct', si otterrà la semantica del valore (quando si passa a una funzione, la funzione ottiene la propria copia indipendente). Se hai bisogno di una semantica di riferimento (quando la passi a una funzione, le modifiche apportate alla funzione vengono viste dal chiamante), quindi usa 'final class'. Sia 'struct' che' final class' proibiscono la sottoclasse, il che rende le cose molto più semplici. (La sottoclasse porta molto pazzo.) –

+0

Ok, grazie! Ho intenzione di fare la finale di classe, infatti è definitiva nel progetto java originale. Ma penso che continuerò a usare la classe invece della struct, ho bisogno di questo progetto per simulare il più possibile il comportamento definito nel progetto java, e cambiare classe per struct potrebbe portare a uno scenario davvero molto grande xD –

Problemi correlati