Kotlin函数参数Val无法重新赋值问题解决(含BST示例)
Kotlin中参数重新赋值的解决方案(附BST节点删除场景)
问题描述
用Kotlin实现二叉搜索树(BST)节点删除的递归函数时,遇到编译错误:Val cannot be reassigned。已知Kotlin函数参数默认是val(不可修改),但需要找到对参数赋值的方法。
错误代码如下(标注错误行):
import kotlin.properties.ReadWriteProperty import kotlin.reflect.KMutableProperty0 data class Node(var value:Int?){ var data:Int? = value var left:Node? = null var right:Node? = null } fun insert(root:Node?, value:Int?):Node{ var root = root if(root==null){ root = Node(value) return root } value?.let { if (root.data!! > value) { root.left = insert(root.left, value) } else { root.right = insert(root.right, value) } } return root } fun inorder(root: Node?){ if(root == null ) return root.data?.let { inorder(root.left) print("${root.data} ") inorder(root.right) } } fun srchDelete(root: Node?, key: Int){ if(root == null){ return } else if (root.data == key){ if(root.left == null && root.right == null){ root.data = null return } if(root.left != null && root.right == null){ root = root.left // 错误:Val cannot be reassigned } } else { srchDelete(root.left, key) srchDelete(root.right, key) } } fun main(){ var values = arrayOf(5,1,3,4,2,7) var root:Node? = null for(i in values){ root = insert(root, i) } srchDelete(root, 2) inorder(root) }
另外,对比Java和Kotlin的数组示例,Java允许在函数内重新赋值参数,Kotlin则不行:
Java示例
import java.util.stream.IntStream; public class DeleteNode { static int[] fun (int[] arr){ arr[0] = 1; arr[1] = 9; arr[7] = 1; arr = new int[9]; // Java允许此操作 arr[0] = 9; return arr; } public static void main(String[] args) { int[] arr = new int[8]; int[] array = fun(arr); IntStream.range(0, arr.length).forEach(h -> System.out.print(" " + arr[h])); System.out.println(); IntStream.range(0, array.length).forEach(h -> System.out.print(" " + array[h])); } }
Kotlin示例
fun list(arr:IntArray):IntArray{ arr[0] = 1 arr[8] = 1 arr = IntArray(8){0} // Kotlin不允许此操作 arr[0] = 1 return arr } fun main(){ var arr = IntArray(9){0} var array = list(arr) for(value in arr) print("$value ") for(value in array) print("$value ") }
请问如何让Kotlin支持这种参数重新赋值操作?
解决方案
1. 将参数转为局部可变变量
这是最直接的处理方式:在函数内部把val参数赋值给一个var类型的局部变量,后续操作这个局部变量即可。
针对BST删除函数的修改示例:
fun srchDelete(root: Node?, key: Int){ var mutableRoot = root // 声明可变局部变量 if(mutableRoot == null){ return } else if (mutableRoot.data == key){ if(mutableRoot.left == null && mutableRoot.right == null){ mutableRoot.data = null return } if(mutableRoot.left != null && mutableRoot.right == null){ mutableRoot = mutableRoot.left // 现在可正常赋值 } } else { srchDelete(mutableRoot.left, key) srchDelete(mutableRoot.right, key) } }
注意:这种方式仅修改局部变量的引用,不会影响函数外部的原变量。如果需要让外部感知到引用变化(比如BST删除后更新根节点),必须让函数返回修改后的节点,就像你的insert函数那样。
正确的BST删除函数应该设计为返回Node?:
fun srchDelete(root: Node?, key: Int): Node? { var mutableRoot = root if (mutableRoot == null) { return null } when { key < mutableRoot.data!! -> mutableRoot.left = srchDelete(mutableRoot.left, key) key > mutableRoot.data!! -> mutableRoot.right = srchDelete(mutableRoot.right, key) else -> { // 处理节点删除的三种情况 if (mutableRoot.left == null) return mutableRoot.right if (mutableRoot.right == null) return mutableRoot.left // 有两个子节点时,取右子树最小节点替换当前节点 var temp = mutableRoot.right while (temp?.left != null) temp = temp.left mutableRoot.data = temp?.data mutableRoot.right = srchDelete(mutableRoot.right, temp?.data!!) } } return mutableRoot }
在main函数中更新根节点:
root = srchDelete(root, 2)
2. 使用包装类传递可变引用
如果需要让函数内部的修改直接影响外部变量,可以用包装类(比如MutableList、自定义包装对象),但这种方式可读性较差,一般不推荐。
示例(用MutableList包装节点):
fun srchDelete(rootWrapper: MutableList<Node?>, key: Int){ val root = rootWrapper[0] if(root == null){ return } else if (root.data == key){ if(root.left == null && root.right == null){ rootWrapper[0] = null return } if(root.left != null && root.right == null){ rootWrapper[0] = root.left // 修改包装类中的引用,外部可感知 } } else { srchDelete(mutableListOf(root.left), key) srchDelete(mutableListOf(root.right), key) } }
调用方式:
val rootWrapper = mutableListOf(root) srchDelete(rootWrapper, 2) root = rootWrapper[0]
3. 理解Kotlin参数不可变的设计意图
Kotlin默认将函数参数设为val,是为了避免因意外修改参数引用导致的bug。Java中允许修改参数引用,但这种修改仅在函数内部有效,外部变量不会受影响(就像你的Java数组示例,外部的arr仍是原数组,函数返回的是新数组)。因此在Kotlin中,更推荐通过返回值传递修改后的引用,而非修改参数本身。
内容的提问来源于stack exchange,提问作者JavaBoi
相关产品推荐
相关产品推荐

