java - 为什么某些 Java 函数能够更改不可变的 Kotlin 对象?

标签 java kotlin collections interop

我惊讶地发现这个程序甚至可以编译,但结果更让我惊讶:

import java.util.Collections.swap

fun main(args: Array<String>) 
{
    val immutableList = List(2) { it } // contents are [0, 1] 
    swap(immutableList, 0, 1)
    println(immutableList) // prints [1, 0]
}
swap函数在库中实现为:
public static void swap(List<?> list, int i, int j) {
        list.set(i, list.set(j, list.get(i)));
    }
List是一个可变的 Java 列表,而不是一个不可变的 Kotlin 列表。所以我认为其他 Java 函数也可以工作。例如:
reverse(immutableList)

工作,但其他人,如fill函数,甚至不编译:
fill(immutableList, 3)

产生以下错误消息:

Type inference failed: fun fill(p0: MutableList!, p1: T!): Unit cannot be applied to (List,Int) Type mismatch: inferred type is List but MutableList! was expected


List fill 的参数然而,函数的类型边界与 reverse 不同。 :
public static <T> void fill(List<? super T> list, T obj)

public static void reverse(List<?> list)

所以似乎没有类型限制,Java 函数可以做任何他们想做的事情。

有人可以解释这怎么可能?这是设计使然,还是只是互操作的限制?

最佳答案

不编译的函数与 kotlin 无关,而是 java 如何处理协变和逆变集合。

来自 Java Generics and Collections

You cannot put anything into a type declared with an extends wildcard—except for the value null, which belongs to every reference type



例如,如果您有以下代码。
List<? extends Number> numbers = new ArrayList<Integer>();

然后你可以这样做
 numbers.add(null);

但如果您尝试执行以下任何操作
numbers.set(0, Integer.valueOf(10)); // case 1
numbers.set(1, numbers.get(0)); // case 2

案例1 编译器不会让你这样做,因为编译器无法知道列表的确切类型,在这种情况下,它是一个整数列表,在其他情况下,它可能会根据某些运行时条件分配一个 double 列表。

案例2 编译器无法确认插入到列表中的对象的类型,并产生错误。您可以使用 Wildcard Capture 解决案例 2 中的问题.

其次是您关于在 java 方法中改变 kotlin 列表的问题。

我们必须了解 kotlin 集合类是相同的旧 Java 集合类,并且 kotlin 没有自己的集合实现。 kotlin 所做的是将 java 集合接口(interface)分为可变和只读。
如果你在 kotlin 中创建一个数组列表,然后检查它的类,你会得到相同的 java.util.ArrayList

现在鉴于 java 没有像 kotlin 那样的可变列表和只读列表的任何概念,因此无法阻止 java 方法改变您的只读 kotlin 列表。因为对于只是一个列表实现的java代码。

以下是书中的相关文字Kotlin in Action

When you need to call a Java method and pass a collection as an argument, you can do so directly without any extra steps. For example, if you have a Java method that takes a java.util.Collection as a parameter, you can pass any Collection or Mutable Collection value as an argument to that parameter.

This has important consequences with regard to mutability of collections. Because Java doesn’t distinguish between read-only and mutable collections, Java code can modify the collection even if it’s declared as a read-only Collection on the Kotlin side. The Kotlin compiler can’t fully analyze what’s being done to the collection in the Java code, and therefore there’s no way for Kotlin to reject a call passing a read-only Collection to Java code that modifies it.

关于java - 为什么某些 Java 函数能够更改不可变的 Kotlin 对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59394115/

相关文章:

android - 杀死导航 Controller 中的 fragment

java - Java 中的 Collections.checkedList() 调用是什么?

java - 在没有 Firebase 控制台的情况下向所有设备发送推送通知

java - 无法获取当前 GPS 位置坐标

java - Spring webflux bean验证不起作用

android - 异步结果的类型是什么?

java - 如何使用java中的TreeMap计算数组右侧较小的元素?

Java Generics - 类本身的子类?

java - 是否可以创建一个适用于 Java 中不同类的泛型方法?

java - for循环中的"cannot be resolved to a variable"