2014-12-05 16 views
5

Se un vincolo di tipo generico Swift è un nome di protocollo, posso richiedere che due tipi, vincolati a quel protocollo, siano dello stesso tipo. Ad esempio:come dire "stessa classe" in un generico Swift

protocol Flier {} 
struct Bird : Flier {} 
struct Insect: Flier {} 
func flockTwoTogether<T:Flier>(f1:T, f2:T) {} 

La funzione flockTwoTogether può essere chiamato con un uccello e un uccello o un insetto e un insetto, ma non con un uccello e un insetto. Questa è la limitazione che voglio. Fin qui tutto bene.

Tuttavia, se provo la stessa cosa con un nome di classe, che non funziona:

class Dog {} 
class NoisyDog : Dog {} 
class WellBehavedDog: Dog {} 
func walkTwoTogether<T:Dog>(d1:T, d2:T) {} 

Il problema è che posso chiamare walkTwoTogether con un WellBehavedDog e NoisyDog. Questo è quello che voglio impedire.

Ci sono due domande qui:

  • C'è un modo per dire che walkTwoTogether non può essere chiamato con un WellBehavedDog e NoisyDog?

  • Si tratta di un errore? Chiedo perché se non riesco a usare un generico per dirlo, è difficile capire perché sia ​​utile che un vincolo generico sia un nome di classe, poiché potremmo ottenere lo stesso risultato solo con una funzione normale.

+0

Mi sembra un comportamento previsto? Hai limitato l'opzione facoltativa di tipo Cane. entrambe le sottoclassi sono conformi a quel tipo e nulla nella dichiarazione le vincola. Se ci fosse un modo per fare quello che vuoi mi aspetterei che richieda una clausola 'where', come in' ', ma che lancia anche un errore. – cmyr

+0

@ cmyr Sì, certo che ci ho provato. Ecco perché ho chiesto se quello che sto facendo non è come farlo, è possibile farlo. :) – matt

+0

Funziona tutto se 'Dog' è un protocollo.I vincoli generici non possono essere applicati ai tipi non di protocollo. * (qualcosa di simile Type Erasure) * – mattt

risposta

3

Non una risposta, di per sé, ma alcuni più dati forse ... Il problema è quando si chiama:

walkTwoTogether(NoisyDog(), WellBehavedDog()) 

Swift può semplicemente trattare entrambi i casi come se fossero casi di Dog (alias, upcast) - abbiamo bisogno di così possiamo chiamare metodi intesi per classe A con sottoclassi di A. (So ​​che lei sa questo.)

Swift non upcast ai protocolli, quindi l'unico modo per farlo è quello di specificare un protocollo per le sottoclassi che la superclasse non conforme a:

protocol Walkable {} 
extension NoisyDog : Walkable {} 
extension WellBehavedDog: Walkable {} 
func walkTwoTogether<T: Dog where T: Walkable>(d1:T, d2:T) { } 

walkTwoTogether(NoisyDog(), WellBehavedDog()) 
// error: type 'Dog' does not conform to protocol 'Walkable' 

il messaggio di errore indica in modo esplicito ciò che sta accadendo - l'unico modo per chiamare questa versione di walkToTogether è quello di upcast le istanze della sottoclasse di Dog, ma Dog non è conforme alle Walkable.

+0

E 'una fortuna, quindi, che non ho presentato il problema come lo avevo originariamente posto a me stesso, ovvero distinguere Dog da NoisyDog! – matt

+0

Nessuna sottoclasse consentita! –

1

direi che questo dovrebbe essere considerato un errore dal momento che inout parametri cambiano i requisiti di tipo come dovrebbero essere:

func walkTwoTogether<T:Dog>(inout d1:T, d2:T) { 

Ora è il comportamento previsto in cui è possibile passare solo due valori dello stesso tipo . (Testato in Swift 1.2 e Swift 2 beta 5)

+0

Sì e no. Questo è legale: 'var d: Dog = WellBehavedDog(); walkTwoTogether (& d, NoisyDog()) 'E in ogni caso, se c'è un bug, penso che il massimo che possiamo dire è che è l'incoerenza; non è chiaro _che_ sia giusto. – matt

+0

@matt Hai ragione potrebbe essere uno dei due. Nel tuo caso devi contrassegnare entrambi i parametri con 'inout'. È interessante notare che si comporta quasi allo stesso modo se non si utilizza un vincolo generico. – Qbyte

+1

Sono più perplesso che mai! Ma il tuo dato è dannatamente interessante, non c'è dubbio. – matt

Problemi correlati