Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin iterable

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?

like image 643
David Avatar asked May 21 '18 15:05

David


People also ask

What is iterable in Kotlin?

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.

What is the difference between iterable and Sequence in Kotlin?

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.

Is array iterable Kotlin?

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.


1 Answers

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.

like image 100
Marko Topolnik Avatar answered Oct 19 '22 15:10

Marko Topolnik