Home > other >  When should I define function return types in kotlin?
When should I define function return types in kotlin?

Time:04-25

Consider the example below where I define two generic functions in the companion object of a generic data class:

data class CommonResponse<T>(
    var data: T? = null
) {
    companion object {
        // WITH return type
        fun <T> succeed1(): CommonResponse<T> = CommonResponse()
        // WITHOUT explicit return type
        fun <T> succeed2() = CommonResponse<T>()
    }
}

With the given context, both functions will have the same return signature. I have no doubt that they will both work correctly, but there is still a question confusing me:

  • Will omitting the return type cause any issues?

CodePudding user response:

What you're trying to describe is the applicability of Type Inference for your function, succeed2. For context:

Kotlin has a concept of type inference for compile-time type information, meaning some type information in the code may be omitted, to be inferred by the compiler. There are two kinds of type inference supported by Kotlin.

  • Local type inference, for inferring types of expressions locally, in statement/expression scope;
  • Function signature type inference, for inferring types of function return values and/or parameters.

Type inference works in situations where the type context contains enough information for the type constraint solver to derive the expected type. So in the case of succeed2 your function return value is giving enough function signature type inference, so it really doesn't matter much in this context.

Argument Against Function Signature Type Inference

If you're trying to control the signature of your function, then you can argue that explicitly declaring the return type will provide you with compile time errors. Example: Let's say we will unsafely (without the help of the IDE) refactor both functions to return AnotherType<T>

data class AnotherType<T>(
    val foo: T
)

data class CommonResponse<T>(
    var data: T? = null
) {
    companion object {
        // THIS WILL FAIL
        fun <T> succeed1(): CommonResponse<T> = AnotherType()
        // THIS WILL SUCCEED
        fun <T> succeed2() = AnotherType<T>()
    }

}

succeed2 function signature type inference will be resolved to AnotherType<T>. (Whereas succeed1 will fail to compile)

CodePudding user response:

This is called a single-expression function.

In regular functions (those with block body), we must declare the return type or it'll be Unit; however, in case of single-expression functions, the compiler infers the return type and generates the same output for both inferred and explicitly declared types.

So technically there's no difference if the inferred type is the type you actually want. (you can also return an ArrayList and declare the type as List, which is another scenario)

However, you might prefer explicitly declaring the return type for the sake of clarity.


Official Kotlin Docs:

Single-expression functions When a function returns a single expression, the curly braces can be omitted and the body is specified after a = symbol:

fun double(x: Int): Int = x * 2

Explicitly declaring the return type is optional when this can be inferred by the compiler:

fun double(x: Int) = x * 2
  • Related