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.
val s: String = ""
Here is an example of a nullable variable. Nullable variables are declared by using the ?.
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:
?. ?: !! requireNotNull and checkNotNull functions Avoid using null checks on non-nullable variables and values that are always null.
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.
val s: String = ""
if (s != null) { doSomething() } // This statement is always true
val s: String = "" doSomething()
fun foo(s: String) {
if (s == null) { // Noncompliant, `s == null` is always false.
doSomething()
}
}
fun foo(s: String) {
doSomething()
}
fun foo(s: String): String {
return s ?: "" // Noncompliant, ?: is useless and the empty string will never be returned.
}
fun foo(s: String): String {
return s
}
If s is nullable, the elvis operation makes sense:
fun foo(s: String?): String {
return s ?: ""
}
fun foo(s: String) {
s!!.doSomething() // Noncompliant, `s` can never be null.
}
fun foo(s: String) {
s.doSomething()
}