Mandatory Annotation for Platform Types

Kotlin platform types are types that Kotlin can’t check at compile-time whether these types are nullable or not, and the compiler will allow all operations on them. However, this can lead to runtime errors, such as NullPointerExceptions, if the platform types are actually null and we try to access their properties or methods. For example, if we have a Java class like this:

public class Client {
  private String name;
  public Client() {}
  public String getName() {
    return name;
  }
}

And we use it in Kotlin like this:

val client = Client()
println(client.name.length)

We will get a NullPointerException, because the name field is null by default, and Kotlin doesn’t warn us about it. To avoid this, we can explicitly declare the type of the name property as nullable in Kotlin:

val client = Client()
val name: String? = client.name
println(name?.length)

This way, we use the safe call operator ?. to access the length property only if the name is not null, and avoid the exception.

However, this solution requires us to manually specify the nullability of each platform type we use in our code, which can be tedious and error-prone. A possible alternative would be to make the type annotation for platform types obligatory or at least make it a compilation warning to not specify the type. This would force or at least warn us to explicitly declare whether we expect a platform type to be nullable or not, and handle it accordingly.

Is it possible to make type annotation (or casting if inline) for platform types mandatory (or at least a warning if not given) at compile time?

val client = Client() // Client is a Java class
println(client.name.length) // Warning: Platform type 'String!' has unknown nullability. Specify the type explicitly or use a safe call operator.

Design

The proposed feature would consist of two parts:

  • A compiler option that enables or disables the warning for platform types without explicit type annotation. The default value of this option would be true, meaning that the warning is enabled by default. Developers could disable it if they want to suppress the warning for some reason.
  • A warning message that informs developers about the platform type they are using and suggests them to specify the type explicitly or use a safe call operator. The message would also include a quick-fix action that automatically adds the type annotation or the safe call operator.

The proposed feature would not affect the existing behavior of platform types in any other way. Platform types would still be treated as neither nullable nor non-null, and all operations would still be allowed on them. The only difference would be that developers would be warned about the potential nullability of platform types and encouraged to handle it explicitly.

Benefits

The proposed feature would have several benefits for Kotlin developers:

  • It would improve the null safety of Kotlin code that interacts with Java code, by preventing runtime errors caused by platform types.
  • It would reduce the manual work required to annotate platform types, by providing a quick-fix action that automatically adds the type annotation or the safe call operator.
  • It would increase the consistency and readability of Kotlin code, by making the nullability of platform types explicit and visible.

Drawbacks

The proposed feature would also have some drawbacks that should be considered:

  • It would introduce a new compiler option that developers would have to configure and maintain.
  • It would generate a lot of warnings for existing Kotlin code that uses platform types without explicit type annotation, which could be annoying or overwhelming for some developers.
  • It would potentially break some existing Kotlin code that relies on the implicit nullability of platform types, which could cause compatibility issues or unexpected behavior.

Alternatives

Some possible alternatives to the proposed feature are:

  • Do nothing and keep the current behavior of platform types. This would avoid introducing any new compiler option or warning, but it would also keep the existing problems of platform types, such as runtime errors, manual work, and inconsistency.
  • Make platform types nullable by default. This would make Kotlin more conservative and safer when interacting with Java code, but it would also make Kotlin more verbose and inconvenient, as developers would have to use a lot of safe call operators or non-null assertions.
1 Like

I like strict rules when coding, so I like the idea. Please be aware you covered only a single case, but there are more similar cases related to platform types:

  1. consumePlatformString(nullableString) - require to explicitly cast to String!?
  2. consumeNonNullString(providePlatformString()) - require to cast to String?
  3. providePlatformStringToConsumer { consumeNonNullString(it) } - require to specify the type explicitly? { it: String -> consumeNonNullString(it) }.
  4. Implementing a Java interface - it is safe to implement a method by using String? in params and String as return type. Can we assume if someone used String/String? then they did this intentionally and we don’t have to show a warning?

One problem with this approach is that if we use the same Java API in multiple places, we have to fix the typing in all places and this is redundant. Alternative would be to improve typing of external API by providing some kind of declaration files, similar to external declarations for JavaScript or to TypeScript declaration files. This way we declare types for API only once and we use it everywhere. Also, we can easily share such declarations with other developers. IDE could help creating such declarations.

2 Likes

I think there is already an IDEA inspection for this

Sounds good. Just a heads-up that Java has plans to make null-restricted types part of the language. Might take a few years, might never happen, but should probably be taken into account. See e.g. Java might eventually get null-restricted types

This concept is quite faulty in my opinion. Non-nullable types should be by default without a need to add any extra characters like !, the same goes for final as immutability should be also be by default. I don’t think that developers would be so eager to add extra characters for every type from now on. For legacy reasons the best thing for them to do is to take a Kotlin-like approach and simply mark legacy code as having unknown nullability and the new one as having a strict one.