rspec/rules/S6619/kotlin/rule.adoc

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]