No more NPE: Converting Java to Kotlin safely

Dmitry Si
ProAndroidDev
Published in
3 min readMar 19, 2020

--

Image by Matt Crampton

So what’s the problem with NullPointerException in Kotlin? Both Google and JetBrains promote Kotlin as safe & interoperable language, not necessarily emphasizing enough that it’s not intrinsically free from NPEs, in fact, if misused Kotlin code will throw as many KNPEs and similar exceptions. How do we convert old Java code into Kotlin safely then?

1. Know the exact sources of the problems

  • Kotlin is not that safe on the boundaries: Nullable/NonNull annotations in Java code by default produce warnings in Java and compiler errors Kotlin and passing null to the NonNull parameter does nothing in Java, until you dereference this null, but it throws a runtime error immediately in Kotlin. That leads to two problems: 1) all ignored warnings or missed annotation can “leak” nulls into Kotlins “null-safe” variables 2) the Java code that was avoiding NPEs can start crashing after 1:1 conversion into Kotlin.
  • Kotlin is not safe on deserialization: serialization is not safe and quite tricky even in Java but Kotlin adds a couple more potential issues to that: default values, sealed classes and nullability itself. Since deserialization may create an instance without calling a user-defined constructor where all values are checked for null and set to defaults the resulting object can have nulls in non-nullable fields. And for the sealed classes it may produce a second instance of Kotlin’s singleton object and make all following when expressions with this object incorrect.
  • Kotlin is not safe on lateinit properties: lateinit properties effectively work exactly the same way as nullable fields in Java, they may or may not reference an object if they don’t — the code will fail in runtime, and the only way to prevent it is to check if the property is initialized.

Now, when the caveats are clear, comes the solution.

2. Follow a formal migration plan

The plan covers the migration of an existing Java class into Kotlin, writing a new Kotlin file would follow a subset of these rules.

1. Annotate all fields, parameters and return values with Nullable/NonNull annotations. Annotation for return values is based on the actual result, and for the public fields and method parameters, the NonNull can be used only if it’s possible to prove that no external code can pass null there. Use Nullable otherwise, or when in doubt. Note that it’s impossible to add annotations to the Java arrays so mark them with a plain comment for future.

2. Fix all null-related warnings that may appear after annotations were added (there isn’t any in the example).

3. Convert the file into Kotlin using IDE or manually. If using the IDE don’t let it “correct” external code, it may produce too many unnecessary changes

4. Add JvmName/JvmStatic annotations to fix the code that’s still used from Java to not produce unnecessary changes in the codebase.

5. Fix all “!!” in the resulting Kotlin file. Note that most of the “!!”s in the auto-generated code are caused by not fixed nullability warnings.

6. Clean up the code.

Here is the complete sequence on Gist.

That’s it! Stay safe!

--

--

Software developer. Most recently Android Java/Kotlin engineer. Former manager, desktop and embedded software creator. https://github.com/fo2rist/