Search code examples
kotlingenericsoverloadingpartial-specialization

How to define a method only when class generic type satisfies a test?


I am trying to do something like the following:

class Event<TPayload>() {

  fun subscribe(handler: (payload: TPayload) -> Unit) { ... }

  fun subscribe(handler: () -> Unit) where TPayload : Unit { ... }

}

The intention is that instances of Event<Unit> will have two overloads of subscribe(), but other instances will only have one.

The above code will not compile. I tried using extension methods, but would have to use a different name for the extra method, rather than overloading it.


Solution

  • You can define that second function as an extension function so it only appears for Events who have a type of Unit. It's okay to overload the function name. Define it outside the class:

    inline fun Event<Unit>.subscribe(crossinline handler: ()->Unit) = 
        subscribe { handler() }
    

    Test:

    class Event<T> {
        private val subscribers = mutableListOf<(T)->Unit>()
    
        fun subscribe(handler: (payload: T) -> Unit) {
            subscribers += handler
        }
    
        fun send(payload: T) {
            for (subscriber in subscribers) subscriber(payload)
        }
    
    }
    
    fun main() {
        val event = Event<Unit>()
        // Using verbose syntax to prove it's the extension function being used
        // and not a lambda with implicit 'it':
        event.subscribe(fun() { println("got unit") })
        event.send(Unit)
    }
    

    If you use a lambda, the compiler will use the first subscribe function with an implicit it parameter since it takes precedence in overload resolution. But runtime behavior would be the same either way if you aren't using the parameter.