如何在不将类定义中val改为var的前提下修改Kotlin对象属性值
实现方案
完全不需要使用JSON序列化/反序列化的中转方案,Kotlin数据类自带的copy()方法就是专门为「不修改原不可变对象、生成修改了指定字段的新实例」场景设计的,全程不需要改动原有类的val属性声明,类型安全、性能远高于JSON方案。
最优方案:使用数据类copy方法
你需求里所有涉及修改的自定义类型都是独立数据类,天生支持copy操作,实现逻辑如下:
- 对
message字段,调用Message类的copy方法,将其中需要替换为postAuthor的作者字段替换为当前Post实例的postAuthor,其余字段自动沿用原对象的值 - 对
visitorsVisitedTimes列表,遍历列表中的每个Pair,保留第一个LocalTime字段,将第二个Author字段替换为当前实例的postAuthor,生成新的列表 - 调用Post类的copy方法,传入修改后的message和visitorsVisitedTimes,得到全新的Post实例,原实例不会有任何改动
示例代码如下(假设Message类中存储作者的字段名为sender,你可以替换为实际定义的字段名):
// input为传入的Post实例 val updatedPost: Post = input.copy( // 处理Message内的作者替换 message = input.message.copy( sender = input.postAuthor ), // 处理访问记录列表内的所有作者替换 visitorsVisitedTimes = input.visitorsVisitedTimes.map { (visitTime, _) -> visitTime to input.postAuthor } ) return updatedPost
这个方案的优势:
- 编译期完成类型校验,不会出现字段名写错、类型不匹配的问题
- 没有额外的序列化/反序列化开销,性能极高
- 原对象全程保持不可变,完全符合你不修改原有类定义、不改动原实例的要求
- 支持任意深度的嵌套修改:如果存在更深层级的嵌套数据类需要替换作者,只需要链式调用对应类的copy方法即可,比如Message内如果嵌套了带作者字段的Reply类,写法为
message = input.message.copy(reply = input.message.reply.copy(author = input.postAuthor))
不推荐的JSON中转方案(仅作参考)
JSON中转方案存在大量隐患:LocalTime等时间类型、Pair等泛型类在没有自定义序列化适配器时很容易出现解析失败,空值处理、字段名映射也容易出问题,性能差,除非有特殊强制要求否则不要使用。
如果非要用这个方案,不能直接操作JSON字符串,需要先将对象解析为结构化的JSON树,修改对应节点后再反序列化:
val gson = Gson() // 先将原对象转为JSON树结构 val jsonTree = gson.toJsonTree(input).asJsonObject // 提前序列化要替换的postAuthor节点 val targetAuthorNode = gson.toJsonTree(input.postAuthor) // 修改Message下的作者字段,注意字段名要和类里的定义完全一致 jsonTree.getAsJsonObject("message").add("sender", targetAuthorNode) // 修改访问列表中每个Pair的second(即Author字段) val visitList = jsonTree.getAsJsonArray("visitorsVisitedTimes") visitList.forEach { item -> item.asJsonObject.add("second", targetAuthorNode) } // 转回Post实例 val updatedPost = gson.fromJson<Post>(jsonTree, Post::class.java) return updatedPost
内容的提问来源于stack exchange,提问作者Light Yagami
相关产品推荐
相关产品推荐

