I’m a beginner to Kotlin, most familiar with Python.
Coming from a dynamically typed language, I became curious with this.
In Kotlin, can a function return either one of multiple types?
For example, can a function return either Int or String type?
+ assuming same function signature (i.e., function name and parameter types), so that we can’t do function overloading
It seems this is also not possible in Java.
Is there something like Union[Int, String]? (which is type hinting from Python 3),
or is there any workaround to it?
I’m interested in this as well and browsed through the thread … and I didn’t get what the conclusion of the discussion is and how one would emulate union types in Kotlin. Any pointers?
The following works but is not typesafe:
fun test(n: Int): Any = if (n >= 0) n else "$n is negative"
Thanks for the reply. So the Union types are currently not supported. I currently can’t really come up with a use case why I would need such kind of function, it was just purely theoretical. But is there actually a workaround to emulate this behavior, other than union types?
sealed class TestResult {
class Int(val value: Int): TestResult()
class String(val value: String): TestResult()
}
fun test(n: Int): TestResult = if (n >= 0) TestResult.Int(n) else TestResult.String("$n is negative")
Next you can use when clause to check all possible types in compile type.
I think this depends on which language you are used to working with. In JavaScript this is quite normal: for many functions you can pass different types to parameters, and the function figures out what to do based on the received type.
I think it is fine if you split the testing of the type and the actual processing for each type into different functions:
fun handle(x: Any) {
when (x) {
is String -> handleString(x)
is Int -> handleInt(x)
...
}
}
A minor correction, because String and Int refer to the inner classes but are supposed to be kotlin.Int and kotlin.String:
sealed class TestResult {
class Int(val value: kotlin.Int): TestResult()
class String(val value: kotlin.String): TestResult()
}
fun test3(n: Int): TestResult =
if (n >= 0) TestResult.Int(n) else TestResult.String("$n is negative")
It is worse, many JavaScript libraries will return something different based upon the value passed in the parameter. An example would be JQuery’s jquery (or $) function that overloads (almost) everything through a single function and then tries to guess what the (generally string) parameter means (is it a tag, is it a selector, is it …?).
I would say that this is a best solution for the problem described in the original message.
Though in official documentation example show it without nesting (which was mandatory prior version 1.1). So with the newer versions the example would look like this.
sealed class TestResult
class TestResultInt(val value: Int): TestResult()
class TestResultString(val value: String): TestResult()
fun test(n: Int): TestResult = if (n >= 0) TestResultInt(n) else TestResultString("$n is negative")
I just don’t really see a real usage for such case. If the initial idea was to return either the result or an error, then I would say some other solution will be better. Usually Kotlin follows Java way to handle error situations: throws an exception
This will return not negative n or throw IllegalStateException with provided message.
fun test(n: Int): Int {
check(n >=0) { "$n is negative" }
return n
}
This will return not negative n or throw IllegalArgumentException with provided message.
fun test(n: Int): Int {
require(n >=0) { "$n is negative" }
return n
}
If custom exception should be thrown, then this will do
fun test(n: Int): Int = if (n >= 0) n else throw MyException("$n is negative")
In some languages instead of using exception to return tuple from the function with result or error. In Kotlin this can be implemented using Pair where for example first element would be expected result and second is possible error or error message. Drawback of this approach is that nullable types should be used.
fun test(n: Int): Pair<Int?, String?> = if (n >= 0) Pair(n, null) else Pair(null, "$n is negative")
val (result, error) = test(someNumber)
if (error != null) {
// do something in case of error
}
checkNotNull(result) // I prefer to use Kotlin's smart cast but it is possible just to use !!