简体   繁体   中英

How to write generic factory method in swift?

I am not sure how to, if it's possible to, write method that calls constructor of it's generic type inheriting from common known base class < T:Base > to create some instances of T without resorting to explicit factory function ie with all bells and whistles provided by type inference.

Example that works in playground:

// Let there be classes MyPod and Boomstick with common Base (not important)
class Base : Printable {
    let value : String; init(_ value : String) { self.value = "Base." + value }
    var description: String { return value }
}
class MyPod : Base {
    init(_ value: String) { super.init("MyPod." + value) }
}
class Boomstick : Base {
    init(_ value: String) { super.init("Boomstick." + value) }
}
// PROBLEM: do not know how to force call of Boomstick(n) instead of Base(n) in here
func createSome<T : Base>() -> T[] {
    var result = Array<T>()
    for n in 1...5 {
        result += T(toString(n))
    }
    return result
}
// This seems to be fine. 
// I was expecting call of createSome<Boomstick>() { ... result += Boomstick(n) ...
let objs : Boomstick[] = createSome() 
// Prints: Base.1, Base.2, ... not much wished Boomstick.1, Boomstick.2, ...
println(objs)

One obvious solution is to delegate creation to caller, but that seems clunky:

func createSome<T>(factory : (Int)->T) { ... }

Thank you.

PS: Isn't assignment of createSome()->Base[] to objs:Boomstick[] type safety violation?

Right now I don't have an answer about the why , but defining a protocol with the initializer only seems to work:

protocol A {
    init(_ value: String)
}

You implement this protocol in all classes as below

class Base : Printable, A {
    let value : String;
    init(_ value : String) { self.value = "Base." + value }
    var description: String { return value }
}

class MyPod : Base, A {
    init(_ value: String) { super.init("MyPod." + value) }
}

class Boomstick : Base, A {
    init(_ value: String) { super.init("Boomstick." + value) }
}

and use A rather than Base in your createSome() func

func createSome<T : A>() -> [T] {
    var result = Array<T>()
    for n in 1...5 {
        result += T(toString(n))
    }
    return result
}

Tested in playground:

let objs : [Boomstick] = createSome()
objs[0]

and it prints:

{value "Base.Boomstick.1"}

Also tried using MyPod and Base and it printed the expected results. Test it out and let me know if it works for you as well.

As of Swift 5 you can write:

protocol Printable: CustomStringConvertible { }

class Base: Printable {
    let value : String;
    required init(_ value : String) { self.value = "Base." + value }
    var description: String { return value }
}

class MyPod : Base {
    required init(_ value: String) { super.init("MyPod." + value) }
}

class Boomstick : Base {
    required init(_ value: String) { super.init("Boomstick." + value) }
}

func createSome<T : Base>() -> [T] {
    var result = Array<T>()
    for n in 1...5 {
        result += [T(String(n))]
    }
    return result
}

let objs : [Boomstick] = createSome()
objs.map{print($0)} // Base.Boomstick.1, Base.Boomstick.2, ...

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