如何修改密封接口属性?Optics未生成对应属性的解决方案探讨
问题
我尝试修改一个密封接口的属性,发现Optics并未为接口的属性生成对应的操作工具,仅能访问子类的SealedType.typeA.value。请问是否必须手动编写如下Lens来实现属性修改,还是可以通过Prism/Iso等其他我忽略的Optics工具来实现?
测试代码
@Test fun `Modify field on sealed interface`() { val orgA = SealedType.TypeA(0) // property [value] is not generated by optics, only `SealedType.typeA.value` exists. val newA = SealedType.value.modify(orgA) { it + 1} expectThat(newA.value).isEqualTo(1) } @optics sealed interface SealedType { val value: Int companion object; @optics data class TypeA( override val value: Int ) : SealedType { companion object } // TypeB ... same as TypeA }
手动编写的Lens示例
companion object { val value: Lens<SealedType, Int> inline get() = Lens( get = { subject -> subject.value }, set = { subject, newValue -> when (subject) { is TypeA -> subject.copy(value = newValue) is TypeB -> subject.copy(value = newValue) } } ) }
解决方案
Optics自动生成工具时,只会针对具体子类的属性生成对应的操作器,不会为密封接口的抽象属性自动生成工具。
是否必须手动编写Lens?
是的,你当前手动实现的Lens是最直接的解决方案。这个Lens<SealedType, Int>需要覆盖所有子类的copy逻辑,而Optics无法自动推断所有子类的实现细节——如果后续新增子类,你还需要手动更新Lens的set分支。
能否用Prism/Iso替代?
- Prism:Prism用于处理父类型到单个子类型的可选转换,只能针对某一个子类操作,无法同时处理所有子类的
value修改,不适用你的场景。 - Iso:Iso用于双向同构的类型转换,密封接口实例包含的信息远不止
value,和Int并非同构关系,因此也不适用。
优化建议
如果后续会新增更多子类,可以在密封接口中定义抽象的copyWithValue方法,让每个子类实现,这样Lens的逻辑可以简化,扩展性更好:
@optics sealed interface SealedType { val value: Int fun copyWithValue(newValue: Int): SealedType companion object; @optics data class TypeA( override val value: Int ) : SealedType { companion object override fun copyWithValue(newValue: Int) = copy(value = newValue) } // TypeB同理实现copyWithValue方法 } // 简化后的Lens实现 companion object { val value: Lens<SealedType, Int> inline get() = Lens( get = { it.value }, set = { subject, newValue -> subject.copyWithValue(newValue) } ) }
内容的提问来源于stack exchange,提问作者zOqvxf
相关产品推荐
相关产品推荐

