9

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?

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
David
  • 7,652
  • 21
  • 60
  • 98

1 Answers1

13

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.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436