Why this doesn't compile?
fun <T> returnOptionalOf(t: T): Optional<T> {
val optional: Optional<T> = Optional.of(t)
return optional
}
The error is
Type mismatch: inferred type is T but T & Any was expected
How to make it work?
This compiles just fine:
fun returnOptionalOfString(t: String): Optional<String> {
val optional: Optional<String> = Optional.of(t)
return optional
}
My first advice would be to just avoid Optional
types in Kotlin. Nullable types offer the same compile-time guarantees about whether a value can hold nulls, they force you to check for null in the right places, and they are more idiomatic (there is also plenty of syntax sugar that will help deal with nullable values, such as ?:
or ?.
operators).
Now back to your specific question, what you see is the combination of several things working against you:
in your function signature, you didn't constrain T
at all. This means that T
could be a nullable type (for instance, String?
), which in turn would mean that this function accepts nulls. Note that Optional.of
is a Java function that expects a non-null value, and throws NPE if given null
as argument. You might want to double check whether throwing NPE is what you intended for your function in this case.
the other (bigger) problem is that you might be assuming that Optional<T>
is covariant in T
, but it's actually invariant (probably because it's a Java class and nothing special was done about it, but there is an issue to change this). To put it more clearly, this means that Optional<A>
is not a subtype of Optional<B>
even if A
is a subtype of B
, and thus you cannot assign an Optional<String>
value to a variable with type Optional<String?>
.
the third thing is that Optional
factory functions like of
and ofNullable
actually return the non-nullable projection of the type you pass, so always Optional<T & Any>
(because a null value will respectively fail or result in an empty Optional
). For instance, Optional.of<String?>("abc")
still returns an Optional<String>
, which doesn't play well with the fact that Optional
is invariant in T
.
This explains why your explicit type (Optional<T>
) of the optional
variable is incorrect. Because Optional.of
returns a Optional<T & Any>
, which cannot be assigned to a variable of type Optional<T>
(even though T & Any
is a subtype of T
).
This might be a lot of jargon, so let me clarify by using your string example, but instead of using String
as T
let's use String?
:
fun returnOptionalOfString(t: String?): Optional<String?> {
val optional: Optional<String?> = Optional.of(t) // error here
return optional
}
Here you'll see the same error for the assignment, but in easier terms.
As @marstran already mentioned, one way to solve all of this is to just use non-nullable types when dealing with Optional
s. In your case, this means constraining T
using : Any
:
fun <T : Any> returnOptionalOf(t: T): Optional<T> {
val optional: Optional<T> = Optional.of(t)
return optional
}