Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to enforce generic type with Kotlin interop

I have method in Kotlin which returns an Rx Observable of a generic list:

public fun getObservable(): Observable<List<FooBar>> {
    return Observable.just(theList)
}

Because the Kotlin list trait is defined as List<out T>, Java will see the return type as Observable<List<? extends FooBar>>.

Is there a way do tell the Kotlin compiler that Java should see this as Observable<List<FooBar>>?

http://kotlinlang.org/docs/reference/generics.html

Updated to show problem correctly.

like image 395
Michael Pardo Avatar asked Feb 12 '15 14:02

Michael Pardo


People also ask

How do I use generic Kotlin?

Generics type checks and castsif (something is List<*>) { something. forEach { println(it) } // The items are typed as `Any?` } The same syntax but with the type arguments omitted can be used for casts that do not take type arguments into account: list as ArrayList .

How do you get the generic parameter class in Kotlin?

There are no direct ways to do this in Kotlin. In order to check the generic type, we need to create an instance of the generic class<T> and then we can compare the same with our class.

Can you instantiate a generic type?

To use Java generics effectively, you must consider the following restrictions: Cannot Instantiate Generic Types with Primitive Types. Cannot Create Instances of Type Parameters. Cannot Declare Static Fields Whose Types are Type Parameters.


2 Answers

You can control how Java sees the Kotlin generics using the JvmWildcard and JvmSuppressWildcards annotation. It was added in Kotlin 1.0 Beta 4 (see announcement).

This change means that your code will already change to be ok, and no longer generate the wildcard. Because the new default is to not use them in many cases, and you can bring them back using the annotations or suppress them when not desired.

The announcement of this change states:

Java Wildcards

There were issues with how Kotlin translated variant types, e.g. whether a List should be List in Java or simply List. Subtleties details aside, we did the following:

  • By default, we do not generate wildcards in return types and where they make no sense
  • When a wildcard is needed, one can enforce its presence with a type annotation: List<@JvmWildcard String> is always List in Java
  • When we need to get rid of a wildcards, we can use @JvmSuppressWildcards (this can be used on a type or any declaration that contains it)

Examples:

fun foo(l: List<String>) // in Java: List<String> (String is final)
fun foo(l: List<@JvmWildcard String>) // in Java: List<? extends String>

interface Open {}
fun bar(p: List<Open>) // in Java: List<? extends Open> (Open is not final)
@JvmSuppressWildcards
fun bar(p: List<Open>) // in Java: List<Open>

So applying this to your question, if you were still having issues:

@JvmSuppressWildcards
public fun getObservable(): Observable<List<FooBar>> {
    return Observable.just(theList)
}
like image 110
Jayson Minard Avatar answered Nov 01 '22 11:11

Jayson Minard


Edit: This behaviour has changed in Kotlin Beta 4. See Jayson Minard's answer.

I can see two options:

  • The first is to return an Observable<MutableList<FooBar>>. As a List is immutable in kotlin, it is declared as List<out T> since objects of type T can only be taken out of it.
    MutableList on the other hand is Java's true List equivalent: since it is mutable it is declared as MutableList<T>.
    So your function would be:

    public fun getObservable(): Observable<MutableList<FooBar>>
            = Observable.just(theList)
    

    The problem with this solution is that should you use it in kotlin, you grant "too much" access to your list if you only wanted to have an Observable of immutable list.

  • The second option is to write a "proxy" method in java that makes the cast once and for all:

    @SuppressWarnings("unchecked")
    public static Observable<List<String>> _getObservable() { return (Observable) TestPackage.getObservable(); }
    

    This is ugly but it works, and it does not break kotlin

    However, I assume you're using RxJava's Observable, so why not use this opportunity to enforce kotlin's immutable list semantic in Java?

    public static Observable<List<String>> _getObservable() {
        return TestPackage.getObservable().map(new Func1<List<? extends String>, List<String>>() {
            @Override
            public List<String> call(List<? extends String> list) {
                return Collections.unmodifiableList(list);
            }
        });
    }
    
like image 44
Salomon BRYS Avatar answered Nov 01 '22 09:11

Salomon BRYS