79 lines
2.5 KiB
Plaintext

== Why is this an issue?
Kotlin provides the operators `as` and `as?` to cast an expression to a specific type,
and `is` to check the assignment compatibility with a type.
These operators are used for downcasts, smart casts, and run-time type checking.
In case the `as` or `as?` operator is used for casting between incompatible types,
that is, distinct types and neither being a subtype of the other,
the cast will never succeed but always throw a `ClassCastException`.
This results in dead code and is likely a symptom of wrong program logic.
Likewise, the `is` operator is redundant and will never return `true` if the type of the expression on the left
side is incompatible with the type on the right.
=== What is the potential impact?
==== Code redundancy
Since the operation will never succeed, it is pointless to use it.
It also leads to dead code branches because `as` will always break the regular control flow with an exception,
while conditions with `is` will never or always be satisfied.
==== Wrong logic
Type casts and type checks that can never succeed are likely
a symptom of wrong program logic.
Developers will not have intended the redundancy of the type check or type cast,
but it might result from an error elsewhere.
== How to fix it
Remove the operator and all dead code branches that result from it,
or investigate why the expression that is type cast or type checked has
an unexpected compile-time type.
=== Code examples
==== Noncompliant code example
[source,kotlin,diff-id=1,diff-type=noncompliant]
----
fun types(value: String, elements: List<String>) {
val a: Int = value as Int // Noncompliant, throws ClassCastException
val b: Int = value as? Int // Noncompliant, will always be null
val text = if (value is Int) { // Noncomplient, then-branch is dead code
"impossible"
} else {
"happens always"
}
}
----
==== Compliant solution
[source,kotlin,diff-id=1,diff-type=compliant]
----
fun types(value: Number, elements: List<Number>) {
val a: Int = value as Int // Compliant, a Number instance could be an Int
val b: Int = value as? Int // Compliant, a Number instance could be an Int
val text = if (value is Int) { // Compliant, both branches reachable
"impossible"
} else {
"happens always"
}
}
----
== Resources
=== Documentation
* https://kotlinlang.org/docs/typecasts.html[Kotlin API Docs, Type checks and casts]
=== Articles & blog posts
* https://en.wikipedia.org/wiki/Liskov_substitution_principle[Wikipedia, Liskov substitution principle]