Question regarding Unit in generic types


#1

Thank you for giving us Kotlin.

See the code listing below. It would be nice if exec in Foo could be written without the input: Unit. Any thought on that or am I missing something?

interface UseCase<T, V> {
    fun exec(input: T): V
}

class Foo : UseCase<Unit, Unit> {
    override fun exec(input: Unit) {
        throw UnsupportedOperationException()
    }
}

class Bar : UseCase<String, String> {
    override fun exec(input: String): String {
        throw UnsupportedOperationException()
    }
}

#2

I don’t think there is much that you can do. Basically, what you’re saying is that exec can have zero or one parameter of unknown type. If you knew the type, you could specify a default value.

How would you be calling exec of type Foo in your example? The closest I have come to what you want would be this:

interface UseCase<T, V> {
    fun exec(input: T? = null): V
}

class Foo : UseCase<Any, Nothing> {
    override fun exec(input: Any?): Nothing {
        throw UnsupportedOperationException()
    }
}

class Bar : UseCase<String?, String> {
    override fun exec(input: String?): String {
        throw UnsupportedOperationException()
    }
}

fun main(args: Array<String>) {
    val bar = Bar()
    bar.exec("Hello world!")

    val foo = Foo()
    foo.exec()
}

But still, that leaves some questions unanswered.


#3

When I write Unit, Unit Kotlin allows me to not write Unit as return type:

class Foo : UseCase<Unit, Unit> {
    override fun exec(input: Unit) {
        throw UnsupportedOperationException()
    }
}

It would be nice to let the same happen for parameters, and have Foo look like this:

class Foo : UseCase<Unit, Unit> {
    override fun exec() {
        throw UnsupportedOperationException()
    }
}

And call it like:

foo.exec()

Instead of

foo.exec(Unit)

Your solution would work, but then a null check will be necessary in every case where Unit is not used (e.g. in Bar).


#4

The return type should probably be Nothing in your example, as the function throws an exception and never returns anything.

Can you show some code how this would be used in practice? How would exec be called with a value of Unit?

val v = ???
... 
foo.exec(v)

I mean, how would Unit end up in v in practice?


#5

Unit will be supplied to the function call:

interface UseCase<T, V> {
    fun exec(input: T): V
}

class Foo : UseCase<Unit, String> {
    override fun exec(input: Unit): String {
        //Go get the string from DB
        return "some string from DB"
    }
}

fun test(){
    val foo = Foo()

    val value = foo.exec(Unit)

    //do something with value

    //compile error:
    //val value2 = foo.exec()
}

Btw using Nothing forces you to write Nothing as return type.


#6

The reason why it’s possible to omit Unit as a return type but not as a parameter type is that a function always has exactly one return value but can have an arbitrary number of parameters. If you wanted to allow to omit Unit as a parameter type, you’d quickly run into all sorts of ambiguities with overloaded methods. (E.g. what if your class also implements an interface which has a no-argument exec method? What does override fun exec actually override?)

And the amount of inconvenience that could be avoided by adding all this complexity is very minor.


#7

I understand :slight_smile:

Thanks for your replies!