Kotlin 数据类中可变集合的防御性副本

2024-05-01

我想要一个接受只读列表的数据类:

data class Notebook(val notes: List<String>) {
}

但可以接受MutableList同样,因为它是List.

例如,以下代码修改传入的列表:

fun main(args: Array<String>) {
    val notes = arrayListOf("One", "Two")
    val notebook = Notebook(notes)

    notes.add("Three")

    println(notebook)       // prints: Notebook(notes=[One, Two, Three])
}

有没有办法对数据类中传入的列表执行防御性副本?


Option 1

您可以使用.toList() http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/to-list.html要制作副本,尽管您将需要内部另一个属性来保存列表副本,或者您需要从数据类移至普通类。

作为数据类:

data class Notebook(private val _notes: List<String>) {
    val notes: List<String> = _notes.toList()
}

这里的问题是你的数据类将有.equals() and .hashCode()基于潜在的变异列表。

所以替代方法是使用普通的类:

class Notebook(notes: List<String>) {
    val notes: List<String> = notes.toList()
}

Option 2

Kotlin 团队也在致力于真正的不可变集合,如果它们足够稳定可以使用,您也许可以预览它们:https://github.com/Kotlin/kotlinx.collections.immutable https://github.com/Kotlin/kotlinx.collections.immutable


Option 3

另一种方法是创建一个允许后代类型的接口MutableList要使用的。这正是杂乱的图书馆 https://github.com/kohesive/klutter通过创建一个轻量级委托类的层次结构来实现,该类可以包装列表以确保不可能发生突变。由于他们使用委托,因此几乎没有开销。您可以使用此库,或者仅查看源代码作为如何创建此类受保护集合的示例。然后您更改方法以请求此受保护版本而不是原始版本。请参阅源代码Klutter ReadOnly 集合包装器 https://github.com/kohesive/klutter/blob/master/core/src/main/kotlin/uy/klutter/core/collections/Immutable.kt and 相关测试 https://github.com/kohesive/klutter/blob/master/core/src/test/kotlin/uy/klutter/core/collections/TestImmutable.kt寻求想法。

作为使用这些类的示例Klutter https://github.com/kohesive/klutter,数据类将是:

data class Notebook(val notes: ReadOnlyList<String>) {

并且调用者将被迫通过传入一个包装列表来遵守,这非常简单:

val myList = mutableListOf("day", "night")
Notebook(myList.toImmutable())  // copy and protect

正在发生的事情是调用者(通过调用asReadOnly())正在制作防御性副本以满足您的方法的要求,并且由于这些类的设计方式,无法随后改变受保护的副本。

Klutter 实现中的一个缺陷是它没有单独的层次结构ReadOnly vs. Immutable所以如果调用者调用asReadOnly()列表的持有者仍然可以引起突变。因此,在您的此代码版本(或 Klutter 的更新)中,最好确保所有工厂方法始终进行复制,并且绝不允许以任何其他方式构造这些类(即创建构造函数)internal)。或者有第二个层次结构,在明确制作副本时使用。最简单的方法就是将代码复制到自己的库中,去掉asReadOnly()方法只留下toImmutable()并使集合类构造函数全部internal.


更多信息

也可以看看: Kotlin 和不可变集合? https://stackoverflow.com/q/33727657/3679676

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Kotlin 数据类中可变集合的防御性副本 的相关文章

随机推荐

  • 以编程方式调用 EntityDeploy 构建任务

    我正在使用 Roslyn 来编译 发出和运行 C 源代码 但是 在面对使用 EntityFramework 的项目时 我遇到了限制 似乎简单地发出编译是不够的 因为有一个EntityDeploy在 DLL 发出后对其进行操作的构建任务 我相
  • 为什么 nginx 接受 Host 标头与 server_name 不匹配的请求?

    我有一个这样设置的站点 nginx 作为代理服务器 通过 UNIX 套接字代理为 Django 站点提供服务的 Gunicorn 实例的请求 这是我的 nginx 配置 server listen 80 server name api my
  • 如何设置带有子域和反向代理且不使用启用站点的 nginxconf?

    我读到没有必要使用sites enabled https stackoverflow com questions 41303885 nginx do i really need sites available and sites enabl
  • 仅使用 .htaccess 在某些页面上将 http 重写为 https

    我知道关于我的问题有很多话题 我检查了所有这些并尝试了它们 但无法使其工作 我只需要在某些页面上将 http 重写为 https 访问 https 页面后 URL 将返回到 http 这是我到目前为止所拥有的 Rewrite Rules f
  • 使用 MahApps.Metro 时应用程序图标拉伸至标题栏高度

    使用 MahApps Metro 时如何防止应用程序图标拉伸到标题栏的高度 无论使用什么尺寸的图标 图标和标题栏边缘之间都没有空格 我也尝试过使用多尺寸图标 但这不起作用 这是一个开箱即用的示例 强烈启发自mahapps punker76
  • SocketIO ERR_CONNECTION_REFUSED

    我开始使用 NodeJS 和 Socket IO 我正在尝试使用 NodeJS http 服务器设置一个基本示例 并建立到服务器的 Socket IO 连接 我也在使用 Angular JS 基本上我想要的是 当用户按下按钮时 就会建立与服
  • 如何将 JWT 承载令牌添加到 API 的所有请求

    我正在尝试组建一个使用 Asp Net Core Identity Identity Server 4 和 Web API 项目的小项目 我的 MVC 项目已使用 IdS4 进行了正确身份验证 从中我获得了一个 JWT 然后我可以将其添加到
  • 通过 WooCommerce WC_Order_Query 通过元数据获取订单

    如何通过订单号 而不是 ID 获取 WooCommerce 订单 我尝试将 wc get orders 与自定义参数一起使用 例如 wc get orders array number gt 1000 但这似乎不起作用 Thanks 订单号
  • 因子不因式分解绘图的 x 轴标签

    我有一个从 Excel 导入的数据框 其中一列的格式为 dates Oct 17 Nov 17 Dec 17 Jan 18 Feb 18 Mar 18 Apr 18 May 18 Jun 18 Jul 18 Aug 18 所有其他列都只是数
  • ConstraintLayout 内的 ImageView 不起作用

    我在正确显示 ImageView 时遇到问题 我想在 ConstraintLayout 中显示 ImageView 在预览中 它看起来完全符合我的需要 但是当我在设备上启动它时 它看起来完全不同 此布局位于回收视图内 这段代码有什么问题
  • Jasmine 不会从资源管道加载 javascript 文件

    我正在开发一个 gem 并且我已经安装了 Jasminehttps github com pivotal jasmine gem https github com pivotal jasmine gem 我所需的所有 JS 文件都位于 ap
  • Jupyter:禁用重启内核警告

    我使用的是 jupyter 4 1 0 我发现自己经常使用 重新启动并运行全部 功能 每次我使用该按钮时 它都会显示此警告 有没有办法禁用该警告 您可以在笔记本中添加一个单元格并使用以下语句 from IPython core displa
  • 在循环内重新声明 for 循环变量时出错

    考虑以下 C 程序片段 for int i 0 i lt 5 i int i 10 lt Note the local variable printf d i 它编译时没有任何错误 并且在执行时给出以下输出 1010101010 但如果我用
  • ios7 中的自动续订订阅

    我知道这个问题已经被问过很多次了 但没有一个问题对我有帮助 而且我不确定这些答案中哪些在 iOS 7 中仍然有效 我正在做一些带有自动续订订阅的应用程序 我已经成功创建了我的产品 我可以与测试用户一起购买它 我的问题是 检查特定用户是否仍然
  • MouseOver CSS3D效果与javascript

    我正在尝试实现这样的 mouseOver 效果 我能够根据每个图块的位置生成所需的 css3d 矩阵 我通过缓慢的鼠标移动实现了这种效果 但是如果我从一个图块快速移动到另一个图块 则它无法正确更新 它在瓷砖之间显示出间隙 更新鼠标悬停时所有
  • 无法弹出 expo 以做出本机反应

    I want to eject expo to bare react native cli but i can t because I am not able to enter android package name when asked
  • 更好的 cout 又名计数;

    伙计们 编写 countn 会很困难 它基本上会在输入的末尾放置换行符 在使用控制台时 这就是我目前所能做的 我发现每次我希望该行成为新行时都编写 n 非常乏味 或者也许它已经实施了 要避免单行上的多次注入 您可以使用临时对象 该临时对象将
  • 使用 selectExpr 选择其中包含特殊字符的 Spark 数据框列

    我所处的场景是我的列名称Munic pio字母上有重音 My selectExpr命令因此失败 有办法解决吗 基本上我有类似以下的表达式 selectExpr CAST Munic pio as string as Munic pio 我真
  • CSS BETWEEN 选择器?

    我理解 相邻选择器 我想做的是根据元素两侧的 div 上的类来更改元素的样式 例如 div class positive div div class divider div div class negative div 正 和 负 类具有不
  • Kotlin 数据类中可变集合的防御性副本

    我想要一个接受只读列表的数据类 data class Notebook val notes List