我想知道为什么这个函数在java中工作,也可以在kotlin中使用,但在kotlin中没有?tailrec
tailrec
简短的答案是因为您的Kotlin方法比JAVA方法“重”。每次调用时,它都会调用另一个“挑衅”的方法 。因此,请参阅下面的更详细的说明。StackOverflowError
Java 字节码等效于 reverseString()
我相应地检查了Kotlin和JAVA中方法的字节码:
JAVA 中的 Kotlin 方法字节码
...
public final void reverseString(@NotNull char[] s) {
Intrinsics.checkParameterIsNotNull(s, "s");
this.helper(0, ArraysKt.getLastIndex(s), s);
}
public final void helper(int i, int j, @NotNull char[] s) {
Intrinsics.checkParameterIsNotNull(s, "s");
if (i < j) {
char t = s[j];
s[j] = s[i];
s[i] = t;
this.helper(i + 1, j - 1, s);
}
}
...
JAVA 中的 JAVA 方法字节码
...
public void reverseString(char[] s) {
this.helper(s, 0, s.length - 1);
}
public void helper(char[] s, int left, int right) {
if (left < right) {
char temp = s[left];
s[left++] = s[right];
s[right--] = temp;
this.helper(left, right, s);
}
}
...
因此,有2个主要区别:
-
Intrinsics.checkParameterIsNotNull(s, "s")
在 Kotlin 版本中,为每个版本调用。helper()
- JAVA 方法中的左索引和右索引递增,而在 Kotlin 中,为每个递归调用创建新索引。
因此,让我们测试一下单独如何影响行为。Intrinsics.checkParameterIsNotNull(s, "s")
测试两个实现
我为这两种情况都创建了一个简单的测试:
@Test
public void testJavaImplementation() {
char[] chars = new char[20000];
new Example().reverseString(chars);
}
和
@Test
fun testKotlinImplementation() {
val chars = CharArray(20000)
Example().reverseString(chars)
}
对于JAVA,测试成功没有问题,而对于Kotlin,由于.但是,在我添加到JAVA方法之后,它也失败了:StackOverflowError
Intrinsics.checkParameterIsNotNull(s, "s")
public void helper(char[] s, int left, int right) {
Intrinsics.checkParameterIsNotNull(s, "s"); // add the same call here
if (left >= right) return;
char tmp = s[left];
s[left] = s[right];
s[right] = tmp;
helper(s, left + 1, right - 1);
}
结论
您的 Kotlin 方法具有较小的递归深度,因为它在每一步都调用,因此比 JAVA 对应物更重。如果您不想要这种自动生成的方法,则可以在编译期间禁用空检查,如此处所述Intrinsics.checkParameterIsNotNull(s, "s")
但是,由于您了解了带来的好处(将递归调用转换为迭代调用),因此您应该使用该调用。tailrec