代码之家  ›  专栏  ›  技术社区  ›  David Berry

如何在泛型接口中引用实现类

  •  3
  • David Berry  · 技术社区  · 6 年前

    假设我想定义一个接口来描述包含类似对象的对象的任何树结构。一个明显的实现方法是:

    interface HasChildren {
        val children: Sequence<HasChildren>
    }
    
    class Branch(val children: Sequence<Branch>) : HasChildren
    

    或多或少的问题是 Branch 必须总是,依次是 分支 ,它已通过接口丢失,我必须显式地将它们类型转换回。

    有没有一种方法,类似于 Self 斯威夫特:

    protocol HasChildren {
        let children : Sequence<Self> { get }
    }
    

    不丢失实现类类型?

    2 回复  |  直到 6 年前
        1
  •  2
  •   Alexey Romanov    6 年前

    这叫做 F-bounded polymorphism (本文是针对scala的,但这不重要;我找不到我喜欢使用java或kotlin的解释,但概念完全相同):

    interface HasChildren<T : HasChildren<T>> {
        val children: Sequence<T>
    }
    
    class Branch(override val children: Sequence<Branch>) : HasChildren<Branch>
    

    如果你熟悉C++,那就知道 "curiously recurring template pattern" 是的。

        2
  •  1
  •   Adam Arold    6 年前

    我想你可能想看看 sealed class 锿。使用它们的树看起来像这样:

    sealed class Node<out T> {
        class Branch<T>(val left: Node<T>, val right: Node<T>) : Node<T>()
        class Leaf<T>(val value: T) : Node<T>()
        object Empty : Node<Nothing>()
    }
    

    密封类 支持检查类型 when 以下内容:

    when(node) {
        is Branch-> {
            // ... 
        }
        is Leaf -> {
            // ... 
        }
        is Empty -> {
            // ...
        }
    }
    

    用法:

    val tree = Branch(
              Leaf("Foo"),
              Branch(Leaf("baz"), Empty))
    

    这个 official docs 也是一个很好的起点。

    编辑: 恐怕你不打字是做不到的。我用一个简单的程序和一个黑客:

    object Playground {
    
        @JvmStatic
        fun main(args: Array<String>) {
            val tree = Branch(
                    Leaf("Foo"),
                    Branch(Leaf("baz"), Empty))
    
            // works
            val foo = tree.left.castTo<Node.Leaf<String>>()
    
            println(foo)
    
            // oops
            tree.left.castTo<Node.Empty>()
        }
    
    }
    
    
    sealed class Node<out T> {
        data class Branch<T>(val left: Node<T>, val right: Node<T>) : Node<T>()
        data class Leaf<T>(val value: T) : Node<T>()
        object Empty : Node<Nothing>()
    }
    
    inline fun <reified U : Node<Any>> Node<Any>.castTo(): U {
        require(this is U) {
            "Node '${this::class.simpleName}' is not of required type '${U::class.simpleName}'."
        }
        return this as U
    }
    

    我真的很想看到一个更健壮的解决方案,因为这相当难看,但到目前为止我还没有找到更好的解决方案(我也不时有这个问题)。