Protocol with Generics and Specialization

Is there a way to define a generic function in a protocol and allow the conforming object to define specializations for that protocol? For example:

protocol Generic {
    func generic<T>(prop: T, otherProp: String)
}

class Gen: Generic {
    func generic<T>(prop: T, otherProp: String) {
        print("generic")
    }

    func generic(prop: String, otherProp: String) {
        print(prop)
    }
}

Now if I use the class like so:

let inst = Gen()
inst.generic(prop: 1, otherProp: "Go")
inst.generic(prop: "Hello", otherProp: "Stop")

I get the expected result of:

generic
Hello

However if I declare inst to be of type Generic:

let inst: Generic = Gen()
inst.generic(prop: 1, otherProp: "Go")
inst.generic(prop: "Hello", otherProp: "Stop")

I get:

generic
generic

So, if I have a property of type Generic, I am unable to use the specialization of the generic function from the implementor of the protocol. Is this expected behavior? Is there a way to achieve the behavior I am looking for, i.e. to use the specialization of the generic function even when accessed through the protocol's interface? I'd appreciate any insight into this. Thanks everyone.

2 answers

  • answered 2019-03-14 09:16 Dávid Pásztor

    If you declare the protocol requirement to be a generic function, you cannot call a more specialised overloaded version of the same function via the protocol type. However, you can specialise the implementation of the generic function for your adopting class by checking the type of the generic input argument.

    class Gen: Generic {
        func generic<T>(prop: T, otherProp: String) {
            if prop is String {
                print(prop)
            } else {
                print("generic")
            }
        }
    }
    
    let inst: Generic = Gen()
    inst.generic(prop: 1, otherProp: "Go")
    inst.generic(prop: "Hello", otherProp: "Stop")
    

  • answered 2019-03-14 13:53 Phelippe Amorim

    You could add the method signature generic(String, String) in the protocol and add a default implementation with an extension:

    protocol Generic {
        func generic<T>(prop: T, otherProp: String)
        func generic(prop: String, otherProp: String)
    }
    
    extension Generic {
        func generic(prop: String, otherProp: String) {
            generic(prop: prop as Any, otherProp: otherProp)
        }
    }