The null safety introduced by Kotlin is amazing. Java was known in the community for Null Pointer Exceptions (NPE), which Kotlin’s safety mechanisms make rare or eliminate entirely. However, one thing that cannot be secured completely is a connection between Kotlin and a language that does not have solid null safety, such as Java or C. Imagine that we use a Java method that declares String as a return type. What type should it be in Kotlin?
If it is annotated with the @Nullable annotation, then we assume it is nullable and we interpret it as String?. If it is annotated with @NotNull, then we trust this annotation and we type it as String. However, what if this return type is not annotated with either of these annotations?
We might say that we should treat such a type as nullable. This would be a safe approach since everything is nullable in Java. However, we often know that something is not null, so we would end up using the non-null assertion !! in many places all around our code.
The real problem would arise when we need to take generic types from Java. Imagine that a Java API returns a List<User> that is not annotated at all. If Kotlin assumed nullable types by default and we did know that this list and those users are not null, we would need to not only assert the whole list but also filter the nulls:
// Kotlin
val users: List<User> = UserRepo().users!!.filterNotNull()
What if a function returned a List<List<User>> instead? This gets complicated:
val users: List<List<User>> = UserRepo().groupedUsers!!
.map { it!!.filterNotNull() }
Lists at least have functions like map and filterNotNull. In other generic types, nullability would be an even bigger problem. This is why instead of being treated as nullable by default, a type that comes from Java and has unknown nullability is a special type in Kotlin: it is called a platform type.
C> Platform type - a type that comes from another language and has unknown nullability.
Platform types are notated with a single exclamation mark ! after the type name, such as String!. Platform types are non-denotable, meaning that one cannot write them explicitly in code. When a value with a platform type is assigned to a Kotlin variable or property, its type can be inferred, but it cannot be explicitly specified. Instead, we can specify either a nullable or a non-nullable type.
// Kotlin
val repo = UserRepo()
val user1 = repo.user // Type of user1 is User!
val user2: User = repo.user // Type of user2 is User
val user3: User? = repo.user // Type of user3 is User?
Thanks to this, getting generic types from Java is not problematic:
val users: List<User> = UserRepo().users
val users: List<List<User>> = UserRepo().groupedUsers
Casting platform types to non-nullable types is better than not specifying a type at all, but it is still dangerous because something we assumed to be non-null might be null. This is why, for safety reasons, I always suggest being very careful when we get platform types from Java. Remember that even if a function does not return null now, that doesn’t mean that it won’t change in the future. If its designer hasn’t specified it with an annotation or by describing it in a comment, this behavior can still be introduced without changing a contract.
If you have some control over Java code that needs to interoperate with Kotlin, introduce @Nullable and @NotNull annotations wherever possible.
This is one of the most important steps when we want to support Kotlin developers well (and it's also important information for Java developers). Annotating many exposed types was one of the most important changes that were introduced in the Android API after Kotlin became a first-class citizen. This made the Android API much more Kotlin-friendly.
Note that many different kinds of annotations are supported, including those by:
JetBrains (@Nullable and @NotNull from org.jetbrains.annotations).
Android (@Nullable and @NonNull from androidx.annotation as well as from com.android.annotations and from the android.support.annotations) support library.
JSR-305 (@Nullable, @CheckForNull and @Nonnull from javax.annotation).
JavaX (@Nullable, @CheckForNull, @Nonnull from javax.annotation).
FindBugs (@Nullable, @CheckForNull, @PossiblyNull and @NonNull from edu.umd.cs.findbugs.annotations).
ReactiveX (@Nullable and @NonNull from io.reactivex.annotations).
Eclipse (@Nullable and @NonNull from org.eclipse.jdt.annotation).
Lombok (@NonNull from lombok).
Alternatively, in Java you can specify that all types should be non-nullable by default using JSR 305’s @ParametersAreNonnullByDefault annotation.
There is something we can do in our Kotlin code as well. My recommendation is to eliminate these platform types as soon as possible for safety reasons. To understand why, think about the difference between how the statedType and platformType functions behave in this example:
// Kotlin
fun statedType() {
val value: String = JavaClass().value
//...
println(value.length)
}
fun platformType() {
val value = JavaClass().value
//...
println(value.length)
}
In both cases, the developer assumed that getValue would not return null, but this is wrong because it results in an NPE in both cases, but there’s a difference in where this error happens.
In statedType, the NPE will be thrown in the same line where we get the value from Java. It would be absolutely clear that we wrongly assumed a non-nullable type and we got null. We would just need to change it and adjust the rest of our code to this change.
In platformType, the NPE will be thrown when we use this value as non-nullable (possibly from the middle of a more complex expression). A variable typed as a platform type can be treated as both nullable and non-nullable. Such a variable might be used a few times safely, but then it gets used unsafely and throws an NPE. When we use such properties, the type system does not protect us. This is a similar situation as in Java, but in Kotlin we do not expect to cause an NPE just by using an object. It is very likely that sooner or later someone will unsafely use a variable type as a platform type, and then we will end up with a runtime exception whose cause might not be easy to find.
// Kotlin
fun statedType() {
val value: String = JavaClass().value // NPE
//...
println(value.length)
}
fun platformType() {
val value = JavaClass().value
//...
println(value.length) // NPE
}
Even more dangerously, a platform type might be propagated further. For instance, we might expose a platform type as a part of our interface:
interface UserRepo {
fun getUserName() = JavaClass().value
}
In this case, the method’s inferred type is a platform type. This means that anyone can still decide if it is nullable or not. One might choose to treat it as nullable in a definition site, and as a non-nullable in the use site:
class RepoImpl : UserRepo {
override fun getUserName(): String? {
return null
}
}
fun main() {
val repo: UserRepo = RepoImpl()
val text: String = repo.getUserName() // NPE in runtime
print("User name length is ${text.length}")
}
Propagating a platform type is a recipe for disaster. They are problematic, so for safety reasons we should always eliminate them as soon as possible. In this case, IDEA IntelliJ helps us with a warning:
Summary
Types that come from another language and have unknown nullability are known as platform types. Since they are dangerous, we should eliminate them as soon as possible and not let them propagate. It is also good to specify types using annotations that specify nullability for exposed Java constructors, methods and fields. This is precious information for Java and Kotlin developers who use these elements.
Marcin Moskala is a highly experienced developer and Kotlin instructor as the founder of Kt. Academy, an official JetBrains partner specializing in Kotlin training, Google Developers Expert, known for his significant contributions to the Kotlin community. Moskala is the author of several widely recognized books, including "Effective Kotlin," "Kotlin Coroutines," "Functional Kotlin," "Advanced Kotlin," "Kotlin Essentials," and "Android Development with Kotlin."
Beyond his literary achievements, Moskala is the author of the largest Medium publication dedicated to Kotlin. As a respected speaker, he has been invited to share his insights at numerous programming conferences, including events such as Droidcon and the prestigious Kotlin Conf, the premier conference dedicated to the Kotlin programming language.
A Kotlin enthusiast since the 1.0 of the language. Android Developer Advocate at Stream. Android/Kotlin tech editor at RayWenderlich.com. Instructor at BME-VIK, teaching Kotlin and Android. Creator of RainbowCake, Krate, and MaterialDrawerKt. Ranked right around the very top of the Kotlin tag on StackOverflow.
After working as a Java engineer for 8 years in various French IT companies, I moved to mobile application development on iOS and Android in 2012. In 2015, I decided to focus on Android and joined i-BP, an IT department of the French banking group BPCE, as Android expert. I am now passionate about Android, clean code, and, of course, Kotlin programming.
I have been developing on and off since I was 10. I started developing full time since I graduated from the University of Utah. I started evaluating Kotlin as a viable language since version 0.6 and have been using it as my primary language since version 0.8. I was part of an influential team that brought Kotlin to my entire organization. I love playing table top games with my family.
I am a mobile and web developer with over 10 years of experience. Currently, I’m specializing in mobile development (both native and cross-platform), but I was working as a frontend and backend developer as well, so I know the development process from each side.
I’m working as a Head of Mobile Development at Mews, so creating the architecture, code reviewing, mentoring, and integrating best practices is a part of my everyday job.
Currently, I’m mainly interested in Flutter. We have an application written in Flutter that is successfully running in production for more than a year.
If you need help with defining the right architecture for your mobile app (both from back-end and front-end sides) or looking for a mentor / code reviewer, feel free to contact me in LinkedIn or Twitter and let’s have a talk.