Should I remove the nullability of overriden methods that are not annotated with nullable in an inherited class

Viewed 2018
Android 3.5.1

I was using the WebView and I noticed that when I override some of the methods all the parameters are nullable types:

webview.webViewClient = object : WebViewClient() {
    override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
         return super.shouldOverrideUrlLoading(view, request)
    }
}

Which means I have to use the safe call operator to use them. However, when I looked at the WebViewClient class that I have overridden the method from they are not specified as nullable annotation in the Java code.

public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
    return shouldOverrideUrlLoading(view, request.getUrl().toString());
}

So I am left thinking do I remove the nullability from the overridden method or keep them?

6 Answers

The source of this issue comes from Interoperability between Java and Kotlin. There are some basic language level differences between Java and Kotlin which causes interoperability issues. Android Studio provides some Lint checks to warn them, such as Unknown Nullness. (reference)


enter image description here

By taking a look at details of Unknown nullness Lint check from android.com, we see that:

To improve referencing code from Kotlin, consider adding explicit nullness information here with either @NonNull or @Nullable.


and on developer.android.com:

If you use Kotlin to reference an unannotated name member that is defined in a Java class (e.g. a String), the compiler doesn't know whether the String maps to a String or a String? in Kotlin. This ambiguity is represented via a platform type, String!.


and on kotlinlang.org:

Any reference in Java may be null, which makes Kotlin's requirements of strict null-safety impractical for objects coming from Java. Types of Java declarations are treated specially in Kotlin and called platform types.


Therefore, when we override a Java method that its arguments are not annotated with nullity annotations, the IDE adds nullable sign (?) for arguments in Kotlin class. It leads to avoid throwing NullPointerException when the method is called in Java by passing a null value for one of the arguments.

webview.webViewClient = object : WebViewClient() {
    override fun shouldOverrideUrlLoading(
        view: WebView, // <- potential to throw NPE before executing the function block!
        request: WebResourceRequest // <- as well!
    ): Boolean {
        return super.shouldOverrideUrlLoading(view, request)
    }
}


In a nutshell, we SHOULD NOT remove ? sign from function arguments, when the overridden method is defined in a Java class.

Unlike Kotlin , Java objects by default can accept null values

@Nullable annotation is just used for operations like code analysers (for eg. if the @Nullable parameter is not handled inside the method then it will show warning)

@NonNull annotation is used to specify that the value received can't/won't be null

if(@NonNull){
      can omit ? check
}else if(@Nullable){
     Mandatory to put ? check
}else(No annotation){
     Not mandatory but put on safer side .
     Passing null from Java into Kotlin fun without ? will lead to NPE
    if(putting ? check){
     java equivalent Kotlin param  (@Nullable Webview view)
    } else{
     java equivalent Kotlin param (@NonNull Webview view)
    }

}

Also Refer this : https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#null-safety

If a virtual method in Java doesn't specify nullability of its parameters somehow, for example with the @Nullable/@NotNull annotations, you are free to choose the nullability either way when overriding that method in Kotlin.

But how should you choose?

  • First, you can consult the method documentation and check the method contract. Does it specify that the method can be called with nulls, and what would these nulls mean when passed to the method?

    In this particular case, WebViewClient.shouldOverrideUrlLoading method doc page doesn't say anything about nulls, so it can be taken as an evidence that its parameters are supposed to be non-nullable.

  • Second, if you are still unsure about the nullability after consulting the docs, you can consider what would you do with the null parameter value, if you receive one. If the only reasonable thing in this situation is to throw an exception, you can delegate that check to the parameter checking code generated by Kotlin—by declaring parameters as non-nullable.

They are not specified as nullable annotation in the Java code.

If that's true note that you risk throwing a NullPointerException if not specified as nullable annotation in the Java code and assign a null value. so remove the nullability from the overridden method if not specified as nullable annotation in the Java code.

For more detail read this also this

Null reference is pretty obvious exception for everybody now because for everything has started with native development on C/C++. Reference to the objects in memory might be missing or cleaned by different reasons. Java was designed in way of those native languages, which assume null pointers everywhere.

Managing all mutable states are getting fun with thousand of microservices. This cause a lot of workaround for Nullable reference. - Optional Objects - Mock of Null Object - Wrappers around references - Annotations, etc. And all this for avoiding changing state of somewhere allocated object.

Finally, Kotlin is not first here. Scala, with immutable states had excellent experience in usage and supporting application. So answering this question and summarize Java was designed in this way from its parent C++, and you should expect null values everywhere. We still checking reference for null, even it's not annotated @Nullable, because of this reason. And in the same way Kotlin handles Java usage, and that is why you need to handle null values in overridden methods.

On the language-level, this can be generalized:

For proper Java interoperability, the Kotlin code should reflect the annotations of the Java code.

The linter only complains about lacking annotations in the other direction, for Kotlin interoperability.

See this recent article on How to write Java friendly Kotlin code?

Related