如何使用 Kotlin 互操作强制泛型类型

How to enforce generic type with Kotlin interop

我在 Kotlin 中有一个方法,它 return 是一个通用列表的 Rx Observable:

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

因为 Kotlin 列表特征定义为 List<out T>,Java 会将 return 类型视为 Observable<List<? extends FooBar>>

有没有办法告诉 Kotlin 编译器 Java 应该将其视为 Observable<List<FooBar>>

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

已更新 以正确显示问题。

编辑:此行为在 Kotlin Beta 4 中已更改。请参阅 Jayson Minard 的

我可以看到两个选项:

  • 首先是return一个Observable<MutableList<FooBar>>。由于 List 在 kotlin 中是不可变的,它被声明为 List<out T> 因为类型 T 的对象只能从它的 out 中取出。
    另一方面,MutableList 是 Java 的真实 List 等价物:因为它是可变的,所以它被声明为 MutableList<T>.
    所以你的函数是:

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

    此解决方案的问题在于,如果您只想在不可变列表中使用 Observable,那么如果您在 kotlin 中使用它,则您授予 "too much" 访问您的列表的权限。

  • 第二个方案是在java中写一个"proxy"方法,使转换一劳永逸:

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

    这很丑陋,但它有效,并且不会破坏 kotlin

    但是,我假设您使用的是 RxJava 的 Observable,那么为什么不利用这个机会在 Java 中强制执行 kotlin 的不可变列表语义呢?

    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);
            }
        });
    }
    

您可以使用 JvmWildcardJvmSuppressWildcards 注释控制 Java 查看 Kotlin 泛型的方式。它是在 Kotlin 1.0 Beta 4 中添加的(参见 announcement)。

此更改意味着您的代码已经更改为正常,不再生成通配符。因为新的默认设置在很多情况下是不使用它们的,您可以使用注释将它们带回来,或者在不需要时抑制它们。

此更改的公告指出:

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>

如果您仍然遇到问题,请将此应用于您的问题:

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