【问题标题】:Kotlin: Can't use GenericTypeIndicator to call Firebase Database's getValueKotlin:无法使用 GenericTypeIndicator 调用 Firebase 数据库的 getValue
【发布时间】:2017-07-21 16:18:19
【问题描述】:

当使用 Kotlin 处理 Firebase 数据库时,如果我使用 GenericTypeIndicator,我似乎无法检索类型为 List<String> 的值,如下所示:

snap.getValue(object : GenericTypeIndicator<List<String>>() {})

它会从 Firebase SDK 产生如下异常:

com.google.firebase.database.DatabaseException: Generic wildcard types are not supported
    at com.google.android.gms.internal.zzbtg.zza(Unknown Source)
    at com.google.android.gms.internal.zzbtg.zza(Unknown Source)
    at com.google.android.gms.internal.zzbtg.zza(Unknown Source)
    at com.google.android.gms.internal.zzbtg.zza(Unknown Source)
    at com.google.firebase.database.DataSnapshot.getValue(Unknown Source)

但是,如果我从 Java 中调用它,如下所示,它可以工作:

snap.getValue(new GenericTypeIndicator<List<String>>() {})

我猜测它与类型具体化有关,所以我这样做了:

inline fun <reified T> genericType() = object: GenericTypeIndicator<T>() {}
val stringListIndicator = genericType<List<String>>()

snap.getValue(stringListIndicator)

但同样的异常发生了。

为什么会这样?


编辑:我尝试使用 jadx-0.6.1 反编译 Java 和 Kotlin 版本。

Java 源代码:

public class Randommmm {
    private static final GenericTypeIndicator<List<String>> ti = new GenericTypeIndicator<List<String>>() {
    };

    public static List<String> x(DataSnapshot snap) {
        return snap.getValue(ti);
    }
}

反编译:

public class Randommmm {
    private static final GenericTypeIndicator<List<String>> ti = new C12761();

    static class C12761 extends GenericTypeIndicator<List<String>> {
        C12761() {
        }
    }

    public static List<String> m48x(DataSnapshot snap) {
        return (List) snap.getValue(ti);
    }
}

Kotlin 源码(一):

object Randommmm {
    private val ti = object : GenericTypeIndicator<List<String>>() {
    }

    fun x(snap: DataSnapshot): List<String> {
        return snap.getValue(ti)
    }
}

反编译:

public final class Randommmm {
    public static final Randommmm INSTANCE = null;
    private static final Randommmm$ti$1 ti = null;

    static class C12761 extends GenericTypeIndicator<List<String>> {
        C12761() {
        }
    }

    static {
        Randommmm randommmm = new Randommmm();
    }

    private Randommmm() {
        INSTANCE = this;
        ti = new Randommmm$ti$1();
    }

    @NotNull
    public final List<String> m48x(@NotNull DataSnapshot snap) {
        Intrinsics.checkParameterIsNotNull(snap, "snap");
        Object value = snap.getValue(ti);
        Intrinsics.checkExpressionValueIsNotNull(value, "snap.getValue(ti)");
        return (List) value;
    }
}

public final class Randommmm$ti$1 extends GenericTypeIndicator<List<? extends String>> {
    Randommmm$ti$1() {
    }
}

按照 Doug 的建议,使用 ArrayList 的 Kotlin 源代码 (2):

object Randommmm {
    private val ti = object : GenericTypeIndicator<ArrayList<String>>() {
    }

    fun x(snap: DataSnapshot): List<String> {
        return snap.getValue(ti)
    }
}

反编译:

public final class Randommmm {
    public static final Randommmm INSTANCE = null;
    private static final Randommmm$ti$1 ti = null;

    static class C12761 extends GenericTypeIndicator<List<String>> {
        C12761() {
        }
    }

    static {
        Randommmm randommmm = new Randommmm();
    }

    private Randommmm() {
        INSTANCE = this;
        ti = new Randommmm$ti$1();
    }

    @NotNull
    public final List<String> m48x(@NotNull DataSnapshot snap) {
        Intrinsics.checkParameterIsNotNull(snap, "snap");
        Object value = snap.getValue(ti);
        Intrinsics.checkExpressionValueIsNotNull(value, "snap.getValue(ti)");
        return (List) value;
    }
}

public final class Randommmm$ti$1 extends GenericTypeIndicator<ArrayList<String>> {
    Randommmm$ti$1() {
    }
}

【问题讨论】:

  • 我在使用 Kotlin 语法时没有遇到任何问题。类型具体化不是问题,因为您正在使用运行时类型创建一个新对象,它有效地实现了与具体类型相同的事情。编辑:但我确实有这个问题 List 而不是 Foo.. 很奇怪。

标签: firebase firebase-realtime-database kotlin kotlin-interop


【解决方案1】:

在 Kotlin 中,

val ti = object : GenericTypeIndicator<List<String>>() {}

生成为(在 Java 中):

SyntethicClass ti = new SyntethicClass();

public final class SyntethicClass extends GenericTypeIndicator<List<? extends String>> {}

注意通配符? extends String,而不是简单的String

请参阅https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#variant-generics 了解说明。

为了防止这种情况,用@JvmSuppressWildcards注释类型参数:

val ti = object : GenericTypeIndicator<List<@JvmSuppressWildcards String>>() {}

这变得超级丑陋,但它有效。或者,按照Doug's answer 使用ArrayList

【讨论】:

  • 非常感谢,这有帮助。提示一点,对于地图,通配符仅用于 dataSnapshot.getValue(object : GenericTypeIndicator&lt;Map&lt;String, @JvmSuppressWildcards Status&gt;&gt;() {}) 之类的值
  • 谢谢!我添加了一些 typaliases 使它不那么难看:typealias UsersMap = Map&lt;String, @JvmSuppressWildcards User&gt;val userTypeIndicator = object : GenericTypeIndicator&lt;UsersMap&gt;() {}
  • 使用Realtime Database Kotlin Extensions,您将不再需要使用GenericTypeIndicator。但是您确实需要该注释。类似于:snap.getValue&lt;List&lt;@JvmSuppressWildcards String&gt;&gt;().
【解决方案2】:

尝试更改此行:

snap.getValue(object : GenericTypeIndicator<List<String>>() {})

到这里:

snap.getValue(object : GenericTypeIndicator<ArrayList<String>>() {})

它对我有用,但我不知道为什么。

【讨论】:

  • 感谢ArrayList 的建议。它在使用 ArrayList 时有效。我试图反编译 Java 代码,以及 Kotlin 的 ListArrayList 版本(我编辑了我的问题,结果可以在上面看到)。似乎List&lt;String&gt; 已转换为List&lt;? extends String&gt;,但ArrayList&lt;String&gt; 仍为ArrayList&lt;String&gt;。那么这似乎是一个 Kotlin 的“问题”。
  • 我找到了解决方案。我把它作为一个答案。
  • 我必须添加一个非空断言:snapshot.getValue(object : GenericTypeIndicator&lt;ArrayList&lt;String&gt;&gt;(){})!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-29
  • 1970-01-01
  • 2018-04-21
  • 1970-01-01
  • 2017-12-20
相关资源
最近更新 更多