138 lines
3.3 KiB
Plaintext
138 lines
3.3 KiB
Plaintext
== Why is this an issue?
|
|
|
|
In Kotlin, nullability is a part of the type system.
|
|
By default, any given type `T` is non-nullable.
|
|
If you append a "?" to the type, it becomes nullable: `T?`.
|
|
|
|
When accessing properties or functions of a nullable type, you need to handle the case when the target is `null`.
|
|
However, while accessing a non-nullable type, it is redundant to test for `null`, as the compiler statically ensures that the value can never be `null`.
|
|
So all the nullability checks on the non-nullable types are considered code smells.
|
|
|
|
On the other hand, performing a null-check on a value that is always null is equally as redundant.
|
|
|
|
Here is an example of a non-nullable variable.
|
|
`s` is of a type `String` and cannot be `null`.
|
|
|
|
[source,kotlin]
|
|
----
|
|
val s: String = ""
|
|
----
|
|
|
|
Here is an example of a nullable variable.
|
|
Nullable variables are declared by using the `?`.
|
|
|
|
[source,kotlin]
|
|
----
|
|
val s: String? = null
|
|
----
|
|
|
|
Explicit null checks are comparing a result to `null` using `==` or `!=` operators.
|
|
In Kotlin, there are various other means of implicitly or explicitly performing a null check or assertion, including the following:
|
|
|
|
- Safe call operator `?.`
|
|
- Elvis operator `?:`
|
|
- Not-null assertion operator `!!`
|
|
- `requireNotNull` and `checkNotNull` functions
|
|
|
|
== How to fix it
|
|
|
|
Avoid using null checks on non-nullable variables and values that are always null.
|
|
|
|
=== Code examples
|
|
|
|
If your variable type is non-nullable, any null checks are redundant. For example, `if (s == null) {}`, `requireNotNull(s)` and `checkNotNull(s)` can be dropped from your code.
|
|
|
|
|
|
==== Noncompliant code example
|
|
|
|
[source,kotlin,diff-id=1,diff-type=noncompliant]
|
|
----
|
|
val s: String = ""
|
|
if (s != null) { doSomething() } // This statement is always true
|
|
----
|
|
|
|
==== Compliant solution
|
|
|
|
[source,kotlin,diff-id=1,diff-type=compliant]
|
|
----
|
|
val s: String = ""
|
|
doSomething()
|
|
----
|
|
|
|
==== Noncompliant code example
|
|
|
|
[source,kotlin,diff-id=2,diff-type=noncompliant]
|
|
----
|
|
fun foo(s: String) {
|
|
if (s == null) { // Noncompliant, `s == null` is always false.
|
|
doSomething()
|
|
}
|
|
}
|
|
----
|
|
|
|
==== Compliant solution
|
|
|
|
[source,kotlin,diff-id=2,diff-type=compliant]
|
|
----
|
|
fun foo(s: String) {
|
|
doSomething()
|
|
}
|
|
----
|
|
|
|
==== Noncompliant code example
|
|
|
|
[source,kotlin,diff-id=3,diff-type=noncompliant]
|
|
----
|
|
fun foo(s: String): String {
|
|
return s ?: "" // Noncompliant, ?: is useless and the empty string will never be returned.
|
|
}
|
|
----
|
|
|
|
==== Compliant solution
|
|
|
|
[source,kotlin,diff-id=3,diff-type=compliant]
|
|
----
|
|
fun foo(s: String): String {
|
|
return s
|
|
}
|
|
----
|
|
|
|
If `s` is nullable, the elvis operation makes sense:
|
|
|
|
[source,kotlin]
|
|
----
|
|
fun foo(s: String?): String {
|
|
return s ?: ""
|
|
}
|
|
----
|
|
|
|
==== Noncompliant code example
|
|
|
|
[source,kotlin,diff-id=4,diff-type=noncompliant]
|
|
----
|
|
fun foo(s: String) {
|
|
s!!.doSomething() // Noncompliant, `s` can never be null.
|
|
}
|
|
----
|
|
|
|
==== Compliant solution
|
|
|
|
[source,kotlin,diff-id=4,diff-type=compliant]
|
|
----
|
|
fun foo(s: String) {
|
|
s.doSomething()
|
|
}
|
|
----
|
|
|
|
|
|
== Resources
|
|
|
|
=== Documentation
|
|
|
|
* https://kotlinlang.org/docs/null-safety.html#nullable-types-and-non-null-types[Kotlin Documentation - Null Safety]
|
|
* https://kotlinlang.org/docs/strings.html[Kotlin Documentation - Strings]
|
|
|
|
=== Articles & blog posts
|
|
|
|
* https://blog.logrocket.com/complete-guide-null-safety-kotlin/[A complete guide to null safety in Kotlin]
|