简体   繁体   中英

Returning runtime dependent Generic in Swift

I want to define a method that returns a generic class depending on class-internal configuration.

For example:

class MyClass {

    private var returnString: Bool

    func array<E>() -> Array<E> {
        if returnString {
            return ["Hello World"] as! [E]
        } else {
            return [1] as! [E]
        }
    }

    func changeThings() {
        returnsString = !returnsString
    }

    init(returnString: Bool) {
        self.returnString = returnString
    }

}

let myClass = MyClass(returnString: true)
let myStrings = myClass.array() // Generic parameter 'E' could not be inferred

How can I accomplish such a thing, only in a working version. I assume Generics are not the way to go, as my use-case needs runtime evaluation? Any way I could use type(of:) and such things?

My goal is to have a “clean” API with a type safe array() call where [E] can be inferred, like this:

let myStrings = myClass.array()

This is a misunderstanding of what generics are. This line of code doesn't do what you seem to expect:

func array<E>() -> Array<E>? {

This says, "for any type passed by the caller, this function will return an optional array of that type." No matter the type. Your function returns either an [String]? or [Int]? , which are only two possible types. Your function promised it could handle anything .

Types are promises, and you need to keep your promise here. You have a function that returns either an array of Strings or an array of Int. You can express "either" using an enum:

enum ReturnType {
    case string([String])
    case int([Int])
}

And then your function returns that:

func array() -> ReturnType {
    if returnString {
        return .string(["Hello World"])
    } else {
        return .int([1])
    }
}

And your calling code gets .string(["Hello World"]) :

let myClass = MyClass(returnString: true)
let myStrings = myClass.array()

If myStrings were instead sometimes [String] and sometimes [Int] , what would the line of code myStrings[0].count do (there is no .count on Int)? Should it crash? The whole point of static types is to determine if something is safe at compile time rather than just crashing at runtime.

You could also adjust your promise. For example, you might say "this returns an array of things that can be converted to strings."

func array() -> [CustomStringConvertible] {
    if returnString {
        return ["Hello World"]
    } else {
        return [1]
    }
}

And then you could safely call description on the results. Or you could assign your own protocol that provided whatever functions you need.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM