79 lines
2.5 KiB
Plaintext
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]
|