2015-10-07 22 views
5

errore: Protocollo requisito 'protocollo' 'esempio' non può essere soddisfatta da una classe non definitiva ('Classe') perché utilizza 'Self' in un non-parametro, non risultato tipo di posizioneCome implementate i metodi di protocollo che restituiscono Selfs covarianti?

protocol Protocol { 
    var instance: Self {get} 
} 

class Class: Protocol { 
    var instance: Class {return Subclass()} 
} 

class Subclass: Class {} 

Ecco come esprimerei ciò che voglio, in C#. (C# non ha, per quanto sappia, un modo per far osservare che il parametro generico "Sé" è in realtà il Sé che conosciamo da Swift, ma funziona abbastanza bene come documentazione che dovrebbe farmi fare la cosa giusta.)

interface Protocol<Self> where Self: Protocol<Self> { 
    Self instance {get;} 
} 

class Class: Protocol<Class> { 
    public Class instance {get {return new Subclass();}} 
} 

class Subclass: Class {} 

... come che potrebbe apparire in una futura versione di 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 {} 

Come sto emulando la porzione di ciò che è rilevante per il mio problema:

protocol Protocol: ProtocolInstance { 
    static var instance: ProtocolInstance {get} 
} 

protocol ProtocolInstance {} 


class Class: Protocol { 
    static var instance: ProtocolInstance {return Subclass()} 
} 

class Subclass: Class {} 

E, qui è quello che ritengo essere la quota di competenza del mio codice:

protocol Protocol { 
    static var : Self? {get} // an existing instance? 
    static var : Self {get} // a new instance 

    func instanceFunc() 
} 

extension Protocol { 
    static func staticFunc() { 
     (??).instanceFunc() 
    } 
} 
+0

Sembra un bug: è possibile creare costrutti simili per metodi di istanza, ma non per metodi di classe o proprietà statiche. [Lo hai archiviato?] (Http://bugreport.apple.com) – rickster

+0

Non riesco a farlo compilare neanche. Modificato. – Jessy

+0

Credo che il codice di esempio aggiornato da C# sia esattamente equivalente al mio codice 'typealias InstanceType'. C'è qualche differenza comportamentale? Se hai chiamato 'Subclass.instance()' nel tuo C#, il tipo restituito (non l'implementazione, ma il tipo di variabile) non sarebbe 'Class'? –

risposta

6

Come si dice, non si può fare questo, e per una buona ragione. Non puoi provare che manterrai la tua promessa. Considerate questo:

class AnotherSubclass: Class {} 
let x = AnotherSubclass().instance 

Quindi x dovrebbe essere AnotherSubclass secondo il protocollo (che è Self). Ma in realtà sarà Subclass, che è un tipo completamente diverso. Non è possibile risolvere questo paradosso a meno che la classe non sia final. Questa non è una limitazione di Swift. Questa limitazione esisterebbe in qualsiasi sistema di tipo corretto perché consente una contraddizione di tipo.

D'altra parte, qualcosa che puoi fare è promettere che instance restituisce un tipo coerente in tutte le sottoclassi (cioè la superclasse). Lo fai con un tipo associato:

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 

Ora x è senza ambiguità di tipo Class. (Capita anche che sia casuale un'altra sottoclasse, che è piuttosto strana, ma è quello che dice il codice.)

BTW, tutto questo di solito suggerisce che stai usando la sottoclasse quando davvero non dovresti esserlo. La composizione e i protocolli risolverebbero probabilmente meglio questo problema in Swift. Chiediti se c'è qualche ragione per cui lo Subclass debba effettivamente essere una sottoclasse di Class. Potrebbe essere un tipo indipendente conforme allo stesso protocollo? Tutti i tipi di problemi scompaiono quando si eliminano le sottoclassi e si concentrano sui protocolli.


Ho pensato a questo di più, e potrebbe esserci un modo per ottenere ciò che stai cercando. Piuttosto che dire che tutte le sottoclassi implementano instance, allegare instance come estensione. Puoi comunque sostituirlo se vuoi restituire qualcos'altro.

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

Questo evita il problema di successione (non è possibile creare lo stesso "AnotherClass restituire il tipo sbagliato" in questo modo).

+0

La promessa che voglio fare è che l'istanza restituisca un Sé o un S: Sé; Chiedo scusa se dire "Sé" non lo ha chiarito, ma se c'è una sintassi che esprime ciò che voglio, non lo so ancora. – Jessy

+0

Non puoi mantenere questa promessa con le lezioni. Vedi il mio esempio 'AnotherSubclass'. Questo non è un problema di sintassi. È un problema di tipi. Non c'è modo di esprimere ciò che stai dicendo senza contraddizione. Se 'Class',' Subclass' e 'AnotherSubclass' erano tutte struct (o semplicemente non avevano ereditarietà), allora tutti potevano implementare' instance' coerentemente. Ma quando ti metti in eredità, non puoi più mantenere la tua promessa. –

+0

Grazie. Vedo ora che il problema deriva dal non essere in grado di esprimere che una classe implementa un protocollo senza dire anche che le sue sottoclassi implementano anche quel protocollo. (Lo sapevo, ma fino ad ora non avevo riscontrato alcun problema.) Suppongo che questo sia qualcosa che verrà risolto in futuro, man mano che il concetto di protocolli evolve. Inoltre, per quanto ne so, devo occuparmi di ereditarietà, al fine di utilizzare entità di Core Data astratte. – Jessy

2

Questo sarebbe in realtà un senso e lavorare, se non si vuole tornare in realtà Self per ogni sottoclasse in questo modo:

protocol Protocol : class { 
    typealias Sub : Self 
    var instance: Sub {get} 
} 

Il che significa che il protocollo definisce una typealias che deve essere una sottoclasse di se stessa . Il codice seguente sarebbe solo lavorare:

class Class: Protocol { 
    var instance: Class {return Subclass()} 
} 

class Subclass: Class {} 

Class().instance // Returns SubClass() 

Tuttavia il codice di cui sopra non viene compilato con l'errore

error: inheritance from non-protocol, non-class type '`Self`' 

che credo sia un bug, perché Self è dichiarato come un tipo di classe. Tuttavia è possibile rendere in qualche modo funziona così:

protocol Protocol : class { 
    typealias Sub : Class 
    var instance: Sub {get} 
} 

ma poi non hanno molto del protocollo stesso, perché solo la classe stessa deve mai conformarsi ad esso.

Problemi correlati