The problem
Why does this not compile in Java?
Optional<Integer> optionalInteger = Optional.of(1);
Optional<Number> optionalNumber = optionalInteger;
int i = optionalNumber.get().intValue();
Enter fullscreen mode Exit fullscreen mode
I am usually very conservative when using generics in Java. When I try to “be smart” I usually over-complicate things and end up with @Suppress("Unchecked")
annotations all over the code. Programming language should always be both easy to write and read. So why is it not possible to do the cast in the example above?
I would expect the above to work because:
-
Number
is parent toInteger
class in Java -
optionalNumber
is a producingNumber
The Java “solution”
It can be solved by using the extends
/super
keywords to specify the upper bound.
Optional<? super Integer> optionalInteger = Optional.of(1);
Optional<Number> optionalNumber = (Optional<Number>) optionalInteger;
int i = optionalNumber.get().intValue();
Enter fullscreen mode Exit fullscreen mode
This will work, but we will get Unchecked cast
warning on the second line, which does make sense in the world of Java generics, but it’s still ugly. Also, what does it say to reader when <? super Integer>
is used?
The Kotlin solution
Kotlin has solved this with declaration-site variance using keywords in
/out
. We will simulate the Optional
class (since there isn’t one in Kotlin) with get()
function for this example:
class Optional<out T>(val t: T) {
fun get(): T = t
}
val optionalInteger = Optional(1)
val optionalNumber: Optional<Number> = optionalInteger
val i = optionalNumber.get().toInt()
Enter fullscreen mode Exit fullscreen mode
By annotating the T
parameter with out
keyword we will specify that T
will always be used as output value (e.g. produced), thus it is safe to perform above cast (since the produced value is safe to cast). Also, if you try adding member function fun set(t: T)
you will get a compiler warning, that T
is out
parameter but occurs in in
position. It is easy to write, read and understand.
As I mentioned, Kotlin does not have separate Optional
class. Instead it has some cool language features for null checking that you can find in any other modern language:
haveReadArticle?.giveItA️()
Enter fullscreen mode Exit fullscreen mode
暂无评论内容