2014-07-02 12 views
16

W mojej aplikacji Chcę utworzyć rodzajowego metody, która tworzy tablicę obiektu depening na dany typ T.Generics zadzwonić z typu T w Swift

stworzyłem następującą funkcję:

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 
} 

Teraz chcę przekazać typ, gdy wywołuję metodę, więc wie, który typ powinien tworzyć wewnętrznie. Myślę, że w Java i C# można użyć metody tak:

object.getArray<Document>("key") 

Kiedy ja nazywam go tak, zawsze pojawia się błąd:

Cannot explicitly specialize a generic function 

Więc moja poprawka było określenie dodatkowego parametru zawierający instancję typu T, więc jest automatycznie wykrywa typ:

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 
} 

czy naprawdę nie ma innego sposobu, aby to zachowanie bez przechodzenia nieużywany instancji? Czy może coś niewłaściwie?

dalsze badania

Po odpowiedzią jtbandes miałem trochę więcej badań. Próbowałem wymusić typ, dodając w rozmowie as.

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 

Więc jakoś typ jest zawsze taki typ bazowy nawet mam określony typ ze słowem kluczowym as. Wiem, że przykład nie ma większego sensu, ale był przeznaczony tylko do testów ..

+1

O, żaden system typu Swift nie jest całkowicie uszkodzony (jeszcze jeden raz). Ponadto "działa", jeśli klasa podstawowa jest dziedziczona z NSObject. [tutaj kod] (https://gist.github.com/xlc/ba7f213e1f94cc1cada5) –

+0

Odpowiedź jest bardzo prosta. musisz określić typ w jakiś sposób, więc robisz to jako 'object.getArray (" key ") jako [Document]' – thesummersign

Odpowiedz

4

Myślę, że to błąd.

można obejść poprzez klasy A podklasę NSObject lub oznaczyć konstruktora klasy bazowej z @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) 

Nie wiem, dlaczego @required rozwiązać problem. Ale 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

Dzięki, dzięki @required mogę rozwiązać mój problem! – Prine

6

Powinieneś być w stanie wskazać typ słowem kluczowym as: object.getArray("key") as Document[].

+0

Dzięki za twoją odpowiedź. Zmieniłem moje pytanie i wypróbowałem Twoje rozwiązanie. Ale jakoś zawsze pozostaje w klasie bazowej i nie tworzy wewnętrznie obiektu określonej klasy za pomocą słowa kluczowego "as". – Prine

+0

Nie mogę teraz przetestować więcej, ale możesz rozważyć [zgłoszenie błędu] (http://bugreport.apple.com). – jtbandes

+0

Zgłosiłem zgłoszenie błędu. Zobaczmy, co odpowiada Apple. Opublikuję to tutaj, gdy otrzymam odpowiedź. – Prine

14

Pracowałem wokół to pożyczając od sprawnego funkcjonowania wykonawczego unsafeBitCast.

Jest zadeklarowany jako func unsafeBitCast<T, U>(x: T, _: U.Type) -> U i można go nazwać jako unsafeBitCast(value, MyType).

zastosowane do funkcji byłoby

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

I można nazwać jak ten

getArray("key", Document) 
+2

Myślę, że prawidłowe wywołanie byłoby getArray ("klucz", Document.self) – Ciprian

+0

Tylko wtedy, gdy funkcja generyczna otrzyma więcej niż jeden parametr. Jeśli oczekuje się tylko jednego parametru, to pozwala przekazać dokument – shinji14

+1

To takie dziwne, że musiałbyś przekazać to jako parametr. The getArray ma większy sens z punktu widzenia czytelności. To działało, dzięki! – JamWils

2

trzeba dokonać inicjator required, następnie dodać let realType = T.self linię i zastąpić T() z 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

Aby odpowiedzieć na pierwszą część Twojego pytania

Nie musisz dodać dodatkowy parametr do metody, aby określić typ T jak już bat [T] jako typ zwracany

więc zastąpić ta linia

object.getArray<Document>("key") 

z tym

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

W ten sposób otrzymasz możliwość określenia typu na T.

0

Nie nazywaj to jak

object.getArray<Document>("key") 

Zamiast pominąć "<Dokument>" część, podobnie jak to:

object.getArray("key") 

Naprawiono problem dla mnie.

0

wersję kodu Praca w pytaniu (część druga)

• Init jest wymagane, ponieważ funkcja zwraca rodzajowe osoba Type ( newPerson(as person:T) -> T)

• Mam zastąpić metody klasy func getWorkingHours() przez var workingHours { get } .. Działa równo w bardziej "szybki" sposób.

getWorkingHours() zwraca godzin, jak sama nazwa wskazuje, nie jest osobą ..

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 

Nadzieja to pomaga .. Ten kod jest gotowy do kopiowania/wklejania w plac zabaw.