Why doesn't Kotlin default to nullable for java types?

When a value from java code is used in Kotlin, it has a type annotated with ! which is like saying it may be null. Why take chances though? Why not just make it so that anything. from java code must be null checked? is it just an issue of convenience (just be careful with null values just like in java) or is it because of constraints imposed by the jvm? Or is it something else? If the first reason is at all a factor in this design decision, I have to say, it feels like a copout.

I believe this is explained in the documentation.

Any reference in Java may be null , which makes Kotlin’s requirements of strict null-safety impractical for objects coming from Java.

So yes, it’s basically for convenience.

Feels like there’s a lot of NPE errors that could be avoided if java values defaulted to nullable. I’ve fallen for that trap more than once. It really would help reduce the number of debug cycles.

+1. I especially don’t like that many of the null checks I add produce compiler warnings.

it’s also really easy to ignore warnings like that, which is another thing I don’t like.

Here’s a really great talk about Kotlin types and dives into the reasoning behind this decision: https://www.youtube.com/watch?v=2IhT8HACc2E

3 Likes

Unless you are dealing with a java library that sets values using reflection or you have complex logic inside of your constructor those warnings should only occure in cases where null checks are not necessary because the value can never be null anyways.

You can always add the compiler argument to change all warnings into errors. Also there is a feature request about only change specifc warnings into errors here.


Maybe a solution to this problem could be a compliler option (maybe a compiler plugin). That way it would be possible to change all platform types into nullable types.

This produces a warning that the condition supplier.get() == null is always false. Not true, of course.

import java.util.function.Supplier

fun suppliesNull(supplier: Supplier<Any>) : Boolean =
    supplier.get() == null

that’s fair, and probably what I’d do in a project that I control.

This tends to happen semi-frequently in android, I’ve found.

It’s very easy to commit a foot-gun with libraries such as retrofit.

As an example, consider a field of a network response class that may be null in the response but is not marked as such in Kotlin.

When the network response is used with a null value for that field, the runtime stays silent until you actually use the field, at which point it throws an NPE.

I spent a bunch of time on this kind of bug where a response class defined ages ago had a “non-null” field that crashed the app I was working on, didn’t understand it at all until I looked at the remote api’s reference and found that that field was nullable.

The problem is that you use Supplier<Any> thereby telling kotlin that the return type of get is never null. You have to use Supplier<Any?> instead. Also kotlin should prevent you calling this with suppliers that return null.

A Supplier<Object> implementation written in java can be passed to a Kotlin function that requires a Supplier<Any> . The java supplier interface does not indicate in any way that the return value is non-null, because it’s a Java interface, and so I have to check it.

Yes but from the view of the kotlin type system this is correct. Java passes a Supplier<Any!>(Objetct) to a function expecting Supplier<Any>. This is allowed but also states “I know this is never null and if it is it will fail with an NPE”. If you expect this to be called from java with nullable values you should use Supplier<Any?>. The fact that you want to add a null check already indicates that this is what you should have done.

I’m aware that nullabiltiy in combination with generics and java interop/reflection is not perfect. Due to the fact that neither reflection nor java has a concept of nullability this can never be solved perfectly. But I don’t think treating all platform types as nullable will help much and won’t help at all in your example.