Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics call with Type T in Swift

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:

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
}

Now I want to pass the type when I call the method, so it does know which type it should create internally. I think in Java and C# you can use a method like that:

object.getArray<Document>("key")

When I call it like that, I always get the error:

Cannot explicitly specialize a generic function

So my fix was to define an additional parameter containing an instance of the type T so it does automatically detect the type:

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
}

Is there really no other way to get that behaviour without passing an unused instance? Or am I misunterstanding something?

Further Testing

After the answer of jtbandes I did some more testing. I tried to force the Type by adding the as in the call.

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

So somehow the type is always the base type even I've specified the type with the as keyword. I know the example does not make much sense, but it was just for testing purpose..

like image 313
Prine Avatar asked Jul 02 '14 22:07

Prine


1 Answers

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)
like image 88
Alfonso Avatar answered Sep 22 '22 06:09

Alfonso