【问题标题】:Kotlin compiler's type inference can't choose which method to call (ambiguity with generic types)Kotlin 编译器的类型推断无法选择调用哪个方法(泛型类型有歧义)
【发布时间】:2017-05-20 11:34:35
【问题描述】:

所以,我有一些带有这些签名的 Java 方法(为简单起见,删除了注释和代码主体):

public class JavaClass {
  public static <E extends CharSequence> E join(E... array) { ... }
  public static <E extends CharSequence> E join(CharSequence separator, E... array) { ... }
}

我在 Kotlin 中有一些代码,它调用了“join”方法:

class KtClass {
    fun test(vararg array: String) {
        JavaClass.join(*array)
    }
}

到目前为止一切顺利;它将传播可变参数并调用前一个方法签名。好啊!

例如,如果我想使用“分隔符”参数调用后一个方法签名,就会出现问题:

class KtClass {
    fun test(vararg array: String) {
        JavaClass.join("<br>", *array)
    }
}

此代码无法编译。编译器无法决定调用哪个方法。错误:

错误:(5, 13) Kotlin:在未完成类型推断的情况下无法在以下候选中进行选择: public open fun join(vararg array: String!): String!在 JavaClass 中定义 公共开放乐趣加入(分隔符:CharSequence!,可变参数数组:字符串!):字符串!在JavaClass中定义

我什至不能命名参数,因为 Kotlin 不允许为非 Kotlin 函数命名参数。

编辑: 用 Ja​​va 方法标头中的普通 String 引用替换了 E 泛型类型参数,并且成功了!所以我猜这是类型推断与泛型类型或类似类型的不兼容?



我很确定这一定是 spread operator (*)。但是如果我不使用它,我无法将可变参数array 传递给join 函数。

如何在不接触 Java 代码的情况下解决这个问题?

是的,我知道有 Array.joinToString 扩展函数,但这只能解决这种特殊情况。我需要知道一个通​​用的解决方案。

【问题讨论】:

  • 请检查here 描述的未经检查的演员表是否可以帮助您。

标签: generics kotlin overloading kotlin-interop


【解决方案1】:

我认为这不是 Kotlin 特有的。问题是泛型参数E 的类型为CharSequence,因此您的调用变为类似于join("separator", "word1", "word2"),这确实是模棱两可的,因为E == CharSequence 类型的第一个参数与其他参数。

【讨论】:

  • 但是Java编译器不会发生这种歧义...我可以调用JavaClass.join("some separator", someArray),它会选择正确的方法。
  • 我知道为什么会发生这种情况,因为扩展运算符......但是,AFAIK,这是将 Kotlin 可变参数参数传递给接收可变参数的 Java 方法的唯一方法
  • 认为这里发生的事情是传播运算符去糖化为join("a", "b", "c") 而不是join("a", arrayOfOtherArgs),因此它与Java 调用不同。这不工作join("&lt;br&gt;", array)?我没有可用的相同库,但为了测试我做了这个并且它有效:`val varargs = arrayOf("a", "b", "c'")` 然后String.format("my format string: %s", varargs)。换句话说,据我所知,您可以将 Kotlin 数组传递给不带扩展运算符的 varargs 方法。
  • AFAIK,是的,可以将 Kotlin 数组传递给没有扩展运算符的 varargs 方法,但 Kotlin varargs 方法参数不能,这就是问题所在......正如你所说,它是“去糖”的,由于分隔符与数组元素的类型相同,它不知道调用哪个方法
  • 有趣。我明白你的意思了。对不起,没有答案。顺便说一句,请注意我的String.format 示例存在缺陷,因为String.format 的可变参数是Object 类型,所以它只是将整个数组作为单个参数传递。
【解决方案2】:

看起来您需要在 Java 中创建一个帮助程序类来解决互操作问题。例如:

public class JavaClassInterop {
    public static <E extends CharSequence> E joinSeparatedBy(CharSequence separator,
                                                             E... array) {
        return JavaClass.join(separator, array);
    }
}

然后你可以同时调用:

import JavaClass.join
import JavaClassInterop.joinSeparatedBy

fun main(args: Array<String>) {
    join(*args)
    joinSeparatedBy("<br>", *args)
}

【讨论】:

    【解决方案3】:

    tldr

    您可能不正确地使用可空类型

    我为这个错误苦苦挣扎了一段时间,但最终想出了一个解决方案。我一开始有这个

    user.items.apply {
            removeAll(otherItems)
            removeAll(otherItems2)
        }
    

    items 集合是一个 MutableSet?所以它可以为空,而 otherItems 集合也可以为空。 那么添加之后呢?在应用之前,并将不可为空的集合传递给 removeAll 函数,错误消失了。

    user.items?.apply {
            removeAll(otherItems.orEmpty())
            removeAll(otherItems2.orEmpty())
        }
    

    【讨论】:

      猜你喜欢
      • 2021-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-08
      • 2017-09-08
      • 2018-05-15
      相关资源
      最近更新 更多