【问题标题】:How to mix Generics and Abstract Types in Scala?如何在 Scala 中混合泛型和抽象类型?
【发布时间】:2011-08-14 12:48:14
【问题描述】:

我想执行以下操作,但“Iterable[BASE]”无法编译。在保持 BASE 为抽象类型的同时,正确的做法是什么?

trait Base
trait Meta {
    type BASE <: Base
}

trait EnumBase extends Base with Ordered[EnumBase]
trait EnumMeta extends Meta with Iterable[BASE] {
    override type BASE <: EnumBase
}

trait Manager extends EnumMeta {
    override type BASE <: MetaBase
}

我希望对于扩展 EnumMeta 并重新定义 BASE 的每个特征,该特征将是它自己的 BASE 的 Iterable。到目前为止,我发现我可以这样做:

trait EnumMeta extends Meta with Iterable[EnumBase] {
    override type BASE <: EnumBase
}

trait Manager extends EnumMeta with Iterable[MetaBase] {
    override type BASE <: MetaBase
}

这是唯一的(重复的)方法,还是有更好的方法,只要它不需要返回泛型参数?

[EDIT] 我刚刚发现如果泛型类型参数是自类型,这将不起作用,如 Ordered[EnumBase]。如果您尝试在一个名为 MetaBase 的派生类中对其进行更多限制,您会得到:

illegal inheritance;  self-type MetaBase does not conform to
Ordered[MetaBase]'s selftype Ordered[MetaBase]

【问题讨论】:

    标签: generics scala abstract-data-type


    【解决方案1】:

    由于Iterable 是泛型,因此有强烈的动机选择泛型而不是类型成员,至少对于 EnumMeta 及其他成员而言是这样。您可以像这样从具有类型成员的祖先转到泛型:

    type EnumMeta[B <: Base] extends Meta with Iterable[B] {
      override type BASE = B
    } 
    

    使用几个关系涉及类型成员的特征,您还可以考虑将类型成员放在封闭的上下文中,而不是放在类本身中,像这样

    trait Context {
       type BASE <: Base
       type META <: Meta
       trait Base { def meta: META} 
       trait Meta { def base: BASE}
    }
    
    trait EnumContext extends Context {
       type BASE <: EnumBase
       type META <: EnumMeta
       trait EnumBase extends Base with Ordered[EnumBase] {}
        trait EnumMeta extends Meta  with Iterable[B] {}
    }
    

    (如果在顶层,特征 BaseMeta 为空,则删除它们并只拥有一个抽象成员 type BASE -和/或 type META- 没有约束)

    当你有一个你真正想要使用的上下文时,你可以混合它,或者制作一个扩展它的对象。

    object EnumContext extends EnumContext  {
       type BASE = EnumBase
       type META = EnumMeta
    }
    

    【讨论】:

    • trait Base 和 Meta 仅用于示例;它们在真实代码中具有功能。我花了 试图理解编译器关于我的泛型基础层次结构的晦涩错误消息,尽管事实上我在 Java 中使用泛型自从它们问世以来,我得到了一个等效的工作不到一个小时的抽象类型。鉴于此,如果没有其他方法,我只会在 Scala 中再次使用泛型。
    • 示例代码中 Base 和 Meta 不为空。这只是一个提示,如果它们是空的,它们应该被删除。也许你可以发布一些你想要的 java 等价物。
    【解决方案2】:

    另一种编译代码的方法是使用类型投影,

    trait Base
    trait Meta {
      type BASE <: Base
    }
    
    trait EnumBase extends Base with Ordered[EnumBase]
    
    trait EnumMeta extends Meta with Iterable[Meta#BASE] { // Type projection!
      type BASE <: EnumBase
    }
    
    trait Manager extends EnumMeta {
      // override type BASE <: MetaBase // You haven't defined type MetaBase yet
    }
    

    类型投影Meta#BASE 告诉编译器你指的是哪个BASE

    很遗憾,自引用不起作用,

    trait EnumMeta extends Meta with Iterable[EnumMeta#BASE] { ...
    

    编译器抱怨,

    [error] illegal cyclic reference involving trait EnumMeta
    [error]   trait EnumMeta extends Meta with Iterable[EnumMeta#BASE] {
    [error]                                                      ^
    

    这个限制是不幸的,并且是类型 parameters 相对于类型成员的优势。参考Didierd's answer

    (还有第二个问题,MetaBase 类型未定义,但这与您的问题无关。)

    【讨论】:

    • 好的。所以你可以引用一个抽象类型作为泛型参数,而不是你自己的抽象类型。但是,只需引入另一个改变抽象类型的中间基本特征/类,并通过再次从中间类派生来缩小具有该抽象类型的 Iterable[] 就足够了。但是,与像我一样直接使用 Iterable[] 中的具体类型相比,您并没有真正获得任何好处。至少你有一个选择。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-04
    • 2015-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多