Search code examples
javacollectionskotlininteropnon-nullable

How do I use Java collections of non-nullable elements from Kotlin?


Using the interop automatisms Kotlin has for nullability, a method like this gets the proper signature in Kotlin:

@NonNull
public String foo(@NonNull String bar) {
    return "bar";
}

Now lift the principle to collections (or other generics, I guess):

@NonNull
public Set<String> foo(@NonNull Collection<String> bar) {
    return new HashSet<String>();
}

This shows in Kotlin as

foo(bar: (Mutable)Collection<String!>) : (Mutable)Set<String!>

I'm fine with (Mutable), but how do I get rid of the platform types in the type parameters? That is, how do I write or annotate the Java function so that I see Set<String> (or Set<String?>, for that matter) from Kotlin?

Using Set<@NonNull String> is not allowed ("'@NonNull' not applicable to type use").

If it makes a difference, I use the Findbugs annotations (via Spotbugs). My perspective is that of a (Java) library provider who wants to enable as seamless use from Kotlin as possible.


Solution

  • Turns out it's an implementation issue of the annotations I used, not a conceptual one.

    As of Spotbugs Annotations 3.1.2, the annotations @NotNull and @NonNull are not declared for targets ElementType.TYPE_USE.

    Jetbrains annotations are declared with that flag, to they can be used on type parameters:

    @NotNull
    public Set<@NotNull String> foo(@NotNull Collection<@NotNull String> bar) {
        return new HashSet<>();
    }
    

    While this looks a little unwieldy in Java, we get the desired Kotlin signature:

    foo(bar: (Mutable)Collection<String>) : (Mutable)Set<String>
    

    Note that annotating type parameters requires source language level Java 8+.


    A note on Android and other situations of being stuck at low langauges levels. Since the annotations have retention level CLASS, I would not expect any runtime problems if an app used a thus annotated library. I have not tested it, though; comments appreciated.