【问题标题】:Visit generic heterogeneous collection in a type safe and ergonomic way以类型安全且符合人体工程学的方式访问通用异构集合
【发布时间】:2021-07-06 17:55:57
【问题描述】:

目标是将通用对象(不同类型参数)的列表存储在列表中,并以类型安全且符合人体工程学的方式对其进行操作。

我当前的设计使用访问者模式:

sealed interface Element {
  fun visit(visitor: Visitor)
}

interface Visitor {
  fun <T> accept(elementA: ElementA<T>)
}

data class ElementA<T>(
    // example members, many more in the real code
    val produce : () -> T,
    val doSomething: (T) -> Unit
) : Element {
  override fun visit(visitor: Visitor) = visitor.accept(this)
}

尽管如此,访问者写起来不是很方便,因为必须继承Visitor

fun exampleUse(elements: List<Element>) {
  for (element in elements) {
    element.visit(object : Visitor {
      override fun <T> accept(elementA: ElementA<T>) {
        // We don't care about the actual type T, just that it exists
        elementA.doSomething(elementA.produce())
      }
    })
  }
}

这不是很符合人体工程学,我希望用户只需要编写常规和短代码,例如 element.visit {it.doSomething(it.produce())}.

唯一的要求是:

  • 元素应该可以存储在一个列表中(同构集合)
  • 用法(例如:a.doSomething(a.produce()))应与类分开,因为它们是在具有不同关注点的不同包中定义的。

如果有办法避免访问者样板,那就更好了。

【问题讨论】:

    标签: kotlin generics


    【解决方案1】:

    这似乎满足您的要求(我假设 Element 仅作为所有 ElementA&lt;Something&gt; 的通用超类型存在):

    data class ElementA<T>(
        // example members, many more in the real code
        val produce : () -> T,
        val doSomething: (T) -> Unit
    )
    
    // optional
    typealias Element = ElementA<*>
    
    // or fun exampleUse(elements: List<ElementA<*>>)
    // if you don't create Element
    fun exampleUse(elements: List<Element>) {
      fun <T> use(elementA: ElementA<T>) {
        // We don't care about the actual type T, just that it exists
        elementA.doSomething(elementA.produce())
      }
        
      for (element in elements) {
        use(element)
      }
    }
    

    注意,本地函数也可以是扩展函数:

    fun exampleUse(elements: List<Element>) {
      fun <T> ElementA<T>.use() {
        // We don't care about the actual type T, just that it exists
        doSomething(produce())
      }
        
      for (element in elements) {
        element.use()
      }
    }
    

    【讨论】:

      【解决方案2】:

      我不确定这是否满足您的要求,但我在 Kotlin 中实现了一个类型安全的异构映射:https://github.com/broo2s/typedmap。它与您的示例略有不同,因为它不是列表,而是地图。例如,您可以像这样使用它:

      val map = simpleTypedMap()
      sess += User("alice")
      val user = sess.get<User>()
      

      您仍然可以为每个键类型存储多个项目,但由于这是一个映射,每个项目都必须是唯一可识别的,所以我不确定这是否适合您:

      map[UserKey(1)] = User("alice")
      map[UserKey(2)] = User("bob")
      
      val alice = map[UserKey(1)]
      val bob = map[UserKey(2)]
      

      【讨论】:

      • 事实上,在我的库中添加一个类型化的列表或者更确切地说是一个多图可能是有意义的。它非常适合您的问题,并且比访问者模式更方便和高效,
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多