2014-07-02 10 views
16

Nella mia applicazione Voglio creare un metodo generico che crea una serie di oggetti depening sul dato tipo T.Generics chiamano con il tipo T a Swift

ho creato la seguente funzione:

func getArray<T : ROJSONObject>(key:String) -> T[] { 
    var elements = T[]() 

    for jsonValue in getValue(key).array! { 
     var element = T() 

     element.jsonData = jsonValue 
     elements.append(element) 
    } 

    return elements 
} 

Ora voglio passare il tipo quando chiamo il metodo, in modo che sappia quale tipo deve creare internamente. Penso che in Java e C# è possibile utilizzare un metodo simile:

object.getArray<Document>("key") 

Quando chiamo così, ho sempre l'errore:

Cannot explicitly specialize a generic function 

Quindi il mio fix è stato quello di definire un parametro aggiuntivo contenente un'istanza del tipo T in modo che rileva automaticamente il tipo:

func getArray<T : ROJSONObject>(key:String, type:T) -> T[] { 
    var elements = T[]() 

    for jsonValue in getValue(key).array! { 
     var element = T() 

     element.jsonData = jsonValue 
     elements.append(element) 
    } 

    return elements 
} 

c'è davvero altro modo per ottenere che il comportamento senza passare un'istanza inutilizzato? O sto fraintendendo qualcosa?

ulteriori test

Dopo la risposta di jtbandes ho fatto un po 'di test. Ho provato a forzare il Tipo aggiungendo il as alla chiamata.

class Person { 

    init() { } 

    func getWorkingHours() -> Float { 
     return 40.0 
    } 
} 

class Boss : Person { 
    override func getWorkingHours() -> Float { 
     println(100.0) 
     return 100.0 
    } 
} 

class Worker : Person { 
    override func getWorkingHours() -> Float { 
     println(42.0) 
     return 42.0 
    } 
} 

func getWorkingHours<T : Person>() -> T { 
    var person = T() 
    person.getWorkingHours() 

    return person 
} 

var worker:Worker = getWorkingHours() as Worker 
var boss:Boss = getWorkingHours() as Boss 
worker.getWorkingHours() // prints out 40.0 instead of 42.0 
boss.getWorkingHours() // prints out 40.0 instead of 100.0 

Così in qualche modo il tipo è sempre il tipo di base, anche ho specificato il tipo con la parola chiave as. So che l'esempio non ha molto senso, ma era solo a scopo di test ..

+1

Oh no Il sistema di tipo Swift è completamente guasto (ancora una volta). Inoltre, "funziona" se la classe base è ereditata da NSObject. [codice qui] (https://gist.github.com/xlc/ba7f213e1f94cc1cada5) –

+0

La risposta è molto semplice. devi specificare il tipo in qualche modo in modo da farlo come 'object.getArray (" chiave ") come [Documento]' – thesummersign

risposta

4

Penso che sia un bug.

si può lavorare intorno ad esso, rendendo la classe una sotto-classe di NSObject o contrassegnare costruttore della classe base con @required

import Cocoa 

class A : NSObject { 
    init() { } 
} 
class B : A {} 
class C : A {} 

func Create<T:NSObject>() -> T { 
    return T() 
} 

println(Create() as A) 
println(Create() as B) 
println(Create() as C) 

//<_TtC11lldb_expr_01A: 0x7f85ab717bc0> 
//<_TtC11lldb_expr_01B: 0x7f85ab451e00> 
//<_TtC11lldb_expr_01C: 0x7f85ab509160> 

class D { 
    @required init() { } 
} 

class E : D { 
    init() { } 
} 

class F : D { 
    init() { } 
} 

func Create2<T:D>() -> T { 
    return T() 
} 

println(Create2() as D) 
println(Create2() as E) 
println(Create2() as F) 

//C11lldb_expr_01D (has 0 children) 
//C11lldb_expr_01E (has 1 child) 
//C11lldb_expr_01F (has 1 child) 

Non certo perché @required risolvere il problema. Ma this is the reference

required

Apply this attribute to a designated or convenience initializer of a class to indicate that every subclass must implement that initializer.

Required designated initializers must be implemented explicitly. Required convenience initializers can be either implemented explicitly or inherited when the subclass directly implements all of the superclass’s designated initializers (or when the subclass overrides the designated initializers with convenience initializers).

+1

Grazie, con il richiesto potrei risolvere il mio problema! – Prine

6

Dovresti essere in grado di indicare il tipo con la parola chiave as: object.getArray("key") as Document[].

+0

Grazie per la tua risposta. Ho modificato la mia domanda e ho provato la soluzione. Ma in qualche modo rimane sempre nella classe base e non crea internamente un oggetto della classe specchiata dalla parola chiave "come". – Prine

+0

Al momento non riesco a testare di più, ma potresti considerare [archiviare un bug] (http://bugreport.apple.com). – jtbandes

+0

Ho inviato un bug report. Vediamo cosa sta rispondendo Apple. Pubblicheremo qui quando avrò una risposta. – Prine

14

ho lavorato intorno a questo prendendo in prestito dalla funzione di esecuzione rapida unsafeBitCast.

È dichiarato come func unsafeBitCast<T, U>(x: T, _: U.Type) -> U e lo si può chiamare come unsafeBitCast(value, MyType).

applicati sulla funzione di questo sarebbe

func getArray<T : ROJSONObject>(key:String, _: T.Type) -> T[] { 
    // function stays the same 
} 

e si può chiamare in questo modo

getArray("key", Document) 
+2

Penso che la chiamata corretta sarebbe getArray ("chiave", Document.self) – Ciprian

+0

Solo quando la funzione generica riceve più di un parametro. Se prevede solo un parametro, consente di passare Documento – shinji14

+1

È così strano che sia necessario passare tale parametro come parametro. Il getArray ha più senso dal punto di vista della leggibilità. Questo ha funzionato, grazie! – JamWils

2

Hai bisogno di fare initializer required, quindi aggiungere let realType = T.self linea e sostituirlo con T()realType().

class Person { 
    required init() { } 

    func getWorkingHours() -> Float { 
     return 40.0 
    } 
} 

class Boss : Person { 
    override func getWorkingHours() -> Float { 
     println(100.0) 
     return 100.0 
    } 
} 

class Worker : Person { 
    override func getWorkingHours() -> Float { 
     println(42.0) 
     return 42.0 
    } 
} 

func getWorkingHours<T : Person>() -> T { 
    let realType = T.self 
    var person = realType() 
    person.getWorkingHours() 

    return person 
} 
0

Per rispondere alla prima parte della tua domanda

è necessario non aggiungere un ulteriore parametro al metodo per specificare il tipo di T come già bat [T] come tipo di ritorno

in modo da sostituire questa linea

object.getArray<Document>("key") 

con questo

object.getArray("key") as [Document] 

In questo modo è possibile specificare il tipo su T.

0

Non lo chiamano come

object.getArray<Document>("key") 

Invece, lasciare fuori la parte "<Documento>", in questo modo:

object.getArray("key") 

risolto il problema per me.

0

versione del codice di lavoro nella questione (seconda parte)

• Init è necessario perché una funzione restituisce una persona generica tipo ( newPerson(as person:T) -> T)

• Ho sostituito i metodi della classe func getWorkingHours() di var workingHours { get } .. Funziona allo stesso modo, in un modo più 'swifty'.

getWorkingHours() restituisce ore, come indica il nome, non una persona ..

class Person { 
    required init() {} 
    var workingHours: Float { get { return 40 } } 
} 

class Boss : Person { 
    override var workingHours: Float { get { return 100 } } 
} 

class Worker : Person { 
    override var workingHours: Float { get { return 42 } } 
} 

func getWorkingHours<T : Person>(_ person: T) -> Float { 
    return person.workingHours 
} 

func newPerson<T : Person>(as person: T) -> T { 
    return T() 
} 

// ----------------------------------------------- 

let worker = Worker() 
let boss = Boss() 

print(getWorkingHours(worker)) // prints 42.0 
print(getWorkingHours(boss)) // prints 100.0 

let person = newPersonOfSameClass(as: worker) 

print(getWorkingHours(person)) // prints 42.0 

Speranza che aiuta .. Questo codice è pronto per il copia/incolla nel parco giochi.

Problemi correlati