Given this class:
data class CSVRecord(private val columns: SortedSet<CSVColumn>) : Iterable<String> {
override fun iterator(): Iterator<String> {
return columns.map { it.value }.iterator()
}
}
spotbugs raises an error:
[ERROR] Questionable cast from Collection to abstract class java.util.List in com.example.CSVRecord.iterator() [com.example.CSVRecord] At CSVRecord.kt:[line 15] BC_BAD_CAST_TO_ABSTRACT_COLLECTION
Is this just because spotbugs doesn't support Kotlin, or is my implementation potentially invalid?
Traversing (Iteration) is the most common operation we perform over any collection. Iterators are used to traverse over a collection object. It provides access to the elements of the collection object sequentially without exposing the underlying structure of the collection.
The order of operations execution is different as well: Sequence performs all the processing steps one-by-one for every single element. In turn, Iterable completes each step for the whole collection and then proceeds to the next step.
Java arrays don't implement Iterable , so Kotlin arrays (which are virtually the same) do not as well. We could make our arrays implement Iterable by creating a standalone class which does not relate to Java arrays.
FindBugs processes bytecode and then reverse-looks-up the file and line info. It appears likely that the Kotlin compiler emitted a CHECKCAST
instruction where, from Kotlin's point of view, it is clear that the cast will pass, but the return type declaration of the Java method, or the declared local variable/parameter type, is broader.
This is what I got from IDEA as the decompiled Kotlin bytecode for your function:
@NotNull
public Iterator iterator() {
Iterable $receiver$iv = (Iterable)this.columns;
Collection destination$iv$iv = (Collection)
(new ArrayList(collectionSizeOrDefault($receiver$iv, 10)));
Iterator var4 = $receiver$iv.iterator();
while(var4.hasNext()) {
Object item$iv$iv = var4.next();
CSVColumn it = (CSVColumn)item$iv$iv;
String var11 = it.getValue();
destination$iv$iv.add(var11);
}
return ((List)destination$iv$iv).iterator();
}
You can see what the declaration inline fun map
means on this level: its entire code became a part of your method implementation. The emitted bytecode happens to use a Collection
-typed variable destination$iv$iv
to hold a reference to an ArrayList
. The return
statement casts it to List
, which it's perfectly allowed to, and which is obviously safe.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With