【问题标题】:kotlin custom get immutable list from mutableListkotlin 自定义从 mutableList 获取不可变列表
【发布时间】:2018-11-11 21:47:45
【问题描述】:

我有一个可变列表的自定义 getter 方法,可以使用 Google 的 Guava 库返回一个不可变列表。然后在构造函数中访问这个可变列表。

data class mutableClass(val list: List<Foo>) {

    private val mutableList: MutableList<Foo>
        get() = ImmutableList.copyOf(field)

    init {
       mutableList = mutableListOf()
       list.forEach {
          mutableList.add(it.copy()) // Exception is thrown here.
          // It actually calls its getter method which is an immutable 
          // list, so when init this class, it throw exception                   
       }
    }
}

data class Foo {}

我将它反编译为Java,在init块中,它调用了mutableList的getter方法。 有没有办法调用 mutabbleList 本身而不是 getter 方法?

【问题讨论】:

  • 这是完整的代码吗?由于该属性是私有的,因此拥有一个不可变列表是没有意义的,它根本无法访问。您还必须考虑到您还拥有 list 属性,因此使用工厂实现该类可能是值得的。

标签: kotlin


【解决方案1】:

对我来说,使用 var 而不是 val 字段以及私有 setter 通常效果最好

class Order

class Something() {

    var orders: List<Order> = listOf()
        private set

    fun addOrder(order: Order) {
        orders = orders
            .toMutableList()
            .apply { add(order) }
    }
}

这会将其公开为不可变的,并且只需要一个字段。我们付出的代价是在添加元素时创建新集合的开销。

【讨论】:

    【解决方案2】:

    当我研究这个主题时,它对我来说最有效的解决方案就是通过合同强制执行。如果您正在创建一个可变列表,假设:

    val immutableList  = mutableListOf(
        Randomy.One,
        Randomy.Two,
        Randomy.Three
    ).toList() // We make it immutable?
    

    然后您使用扩展功能或下面给出的任何建议,例如使用ImmutableList.copyOf(field),您可能会因为将项目复制到另一个集合中而付出代价。

    另一种选择是支付拆箱成本,例如:

    val myImmutableList = myMutableList as List<Int>
    

    我选择的解决方案只是通过合同强制执行,这是一个非常简单的概念。您的 MutableList 继承自 List。如果您想共享具有该抽象级别的项目集合,您可以选择通过强制类型来实现:

    val immutableList: List<Randomy> = mutableListOf(
        Randomy.One,
        Randomy.Two,
        Randomy.Three
    )
    

    现在,如果我们与另一个组件共享该列表,我们将免费使用正确的抽象。我们也可以使用 Collection,因为 List 继承自 Collection:

    val immutableList: Collection<Randomy> = mutableListOf(
        Randomy.One,
        Randomy.Two,
        Randomy.Three
    )
    

    【讨论】:

      【解决方案3】:

      Kotlin 标准库选择接口不变性。这意味着,实现的接口决定了引用本身的可变性。

      因此,将MutableList&lt;T&gt; 制作成List&lt;T&gt; 的正确方法是将其装箱,如下所示:

      val myMutableList = mutableListOf(1, 2, 3, 4)
      val myImmutableList = myMutableList as List<Int>
      

      这样,作为List&lt;Int&gt; 中的myImmutableList 引用,它只会暴露来自List&lt;Int&gt; 的成员,而不是那些只是MutableList&lt;Int&gt; 定义的成员,这允许改变对象的状态,因此名单。

      那么,如果你真的想避免下面的问题(从上面的代码中恢复),

      val hackedList = myImmutableList as MutableList<Int>
      

      ...您可以通过拆箱访问可变实现,您可能宁愿选择以下解决方案:

      class ImmutableList<T>(list: MutableList<T>) : List<T> by list
      
      fun <T> MutableList<T>.toImmutable() = ImmutableList(this)
      

      然后按如下方式使用:

      val myMutableList = mutableListOf(1, 2, 3, 4)
      val myImmutableList = myMutableList.toImmutable()
      

      因此,您将避免上述问题。事实上,任何从MutableList&lt;T&gt;.toImmutable() 拆箱返回值的尝试都会以TypeCastException 结束,因为List&lt;T&gt; 的实现不再是MutableList&lt;T&gt;。相反,它是一个ImmutableList&lt;T&gt;,它不会公开任何可能改变对象的方法。

      与@Lucas 方法不同,这样您就不会浪费时间来复制元素,因为您将依赖 Kotlin 中的 by 关键字,它允许您通过现有的实现来实现接口。也就是说,您将传递给ImmutableList&lt;T&gt; 的构造函数的MutableList&lt;T&gt;

      【讨论】:

        【解决方案4】:

        当然它会调用 getter(返回 ImmutableList.copyOf(field))。

        您可以在 init 块中简单地分配给 mutableList 新复制的可变列表:

        data class MutableClass(val list: List<Foo>) {
        
            private val mutableList: MutableList<Foo>
                get() = ImmutableList.copyOf(field)
        
            init {
                mutableList = list.map { it.copy() }.toMutableList()
            }
        }
        

        或者没有init:

        data class MutableClass(val list: List<Foo>) {
        
            private val mutableList: MutableList<Foo> = list.map { it.copy() }.toMutableList()
                get() = ImmutableList.copyOf(field)
        
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-10-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-06-19
          • 2019-06-13
          相关资源
          最近更新 更多