Generics call with Type T in Swift

后端 未结 7 974
逝去的感伤
逝去的感伤 2021-02-12 17:14

In my application I want to create an generic method which creates an array of object depening on the given type T.

I created the following function:

fun         


        
相关标签:
7条回答
  • 2021-02-12 17:58

    Don't call it like

    object.getArray<Document>("key")
    

    Instead, leave out the "<Document>" part, like this:

    object.getArray("key")
    

    Fixed the issue for me.

    0 讨论(0)
  • 2021-02-12 18:00

    Working version of the code in the question ( second part )

    • Init is required because a function returns a generic person Type ( newPerson(as person:T) -> T )

    • I've replaced the class methods func getWorkingHours() by var workingHours { get }.. It works equally, in a more 'swifty' way.

    getWorkingHours() returns hours, as the name indicates, not a Person..

    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
    

    Hope it helps.. This code is ready for copy/paste in playground.

    0 讨论(0)
  • 2021-02-12 18:05

    I think it is a bug.

    You can work around it by making the class a sub-class of NSObject or mark constructor of base class with @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)
    

    Not sure why @required solve the problem. But 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).

    0 讨论(0)
  • 2021-02-12 18:09

    You need to make initializer required, then add let realType = T.self line and replace T() with 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 讨论(0)
  • 2021-02-12 18:13

    To answer the first part of your question

    You need not add an additional parameter to the method to specify the type of T as you already bat [T] as return type

    so you replace this line

    object.getArray<Document>("key")
    

    with this

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

    This way you get an opportunity to specify the type to T.

    0 讨论(0)
  • 2021-02-12 18:18

    I worked around this by borrowing from the swift runtime function unsafeBitCast.

    It's declared as func unsafeBitCast<T, U>(x: T, _: U.Type) -> U and you can call it as unsafeBitCast(value, MyType).

    Applied to your function this would be

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

    And you can call it like this

    getArray("key", Document)
    
    0 讨论(0)
提交回复
热议问题