在特征中扩展特征时,“超级”指的是什么?

时间:2015-11-21 03:22:06

标签: scala inner-classes mixins super traits

我想在一个特征中扩展一个特征,如:

  trait NodeTypes {
    trait Node {
      def allNodesHaveThis: Int
    }
  }

  trait ScrumptiousTypes extends NodeTypes {
    trait Node extends super.Node {
      def scrumptiousness: Int
    }
  }

  trait YummyTypes extends NodeTypes {
    trait Node extends super.Node {
      def yumminess: Int
    }
  }

  object Graph extends NodeTypes with ScrumptiousTypes with YummyTypes {
    case class Node() extends super.Node {
      override def allNodesHaveThis = 1
      override def scrumptiousness = 2  // error: ScrumptiousTypes.Node has been disinherited
      override def yumminess = 3
    }
  }

如果这样做,那么说“当Graph继承自<Whatever>Types时,其Node类必须提供<Whatever>所需的方法,这是一种很好的方式。 “

但是Scala 2.11.2编译器说:

error: method scrumptiousness overrides nothing
      override def scrumptiousness = 2
                   ^

似乎YummyTypes.Node阴影ScrumptiousTypes.Node,遵循Scala解析方法的“菱形”继承的通常方式:按类型线性化。据我所知, 应该是正常的,因为YummyTypes.Node显式扩展super.Node,它通过相同类型的线性化引用ScrumptiousTypes

我误解了什么?或者,super.Node指的是什么?为什么?

如果你想知道为什么我这样做,那么我可以同时将变化混合到几个特征中,所以继承的特征可以互操作,如this question中所述。在最后的Node类(以及它使用的其他类)中,我不希望从每个Node特征显式扩展:我想从一个“东西”(无论它是什么)混合并得到所有相互一致的变化制作Node和其他特征,都在捆绑中。或者,如果一个特征定义了一组Node扩展,那么从ScrumptiousTypes扩展应该使所有Node扩展包含scrumptiousness成员,而不必列出所有Node-extension:trait Hypernode extends ScrumptiousTypes.Node,{ {1}}等等。

3 个答案:

答案 0 :(得分:5)

使用类型也解决问题

trait NodeTypes {

  trait Node {
    def allNodesHaveThis: Int
  }

}

trait ScrumptiousTypes extends NodeTypes {

  trait Node extends super.Node {
    def scrumptiousness: Int
  }

  type ScrumptiousTypesNode = this.Node
}

trait YummyTypes extends NodeTypes {

  trait Node extends super.Node {
    def yumminess: Int
  }

  type YummyTypesNode = this.Node
}

object Graph extends NodeTypes with ScrumptiousTypes with YummyTypes {

  case class Node() extends ScrumptiousTypesNode with YummyTypesNode {
    override def allNodesHaveThis = 1

    override def scrumptiousness = 2

    override def yumminess = 3
  }

}

------ ------- V2 使用对象包含到Node, 但由于路径依赖,这不是一个好主意, 也许这将是问题

trait NodeTypes {

  trait Node {
    def allNodesHaveThis: Int
  }

}

object NodeTypes extends NodeTypes

trait ScrumptiousTypes extends NodeTypes {

  trait Node {
    def scrumptiousness: Int
  }

  type ScrumptiousTypesNode = this.Node
}

object ScrumptiousTypes extends ScrumptiousTypes

trait YummyTypes extends NodeTypes {

  trait Node {
    def yumminess: Int
  }

  type YummyTypesNode = this.Node
}

object YummyTypes extends YummyTypes

trait Nodes {

  trait Nodes extends NodeTypes.Node with  YummyTypes.Node with ScrumptiousTypes.Node

}


object Graph extends  Nodes {

  case class Nodes() extends super.Nodes {
    override def yumminess: Int = 1
//
    override def scrumptiousness: Int = 2

    override def allNodesHaveThis: Int = 3
  }

}

答案 1 :(得分:3)

这是因为班级线条化。见Spec

<强>解释

设C为具有模板C1 with ... with Cn的类。然后,线性化是从Cn到C1的元素的连接,将所有相同的元素替换为左边。这里的元素包括var,val,def,traits,object。

如果您想查看order of linearization,请使用

import scala.reflect.runtime.universe._
val tpe = typeOf[scala.collection.immutable.List[_]]
tpe.baseClasses foreach { s => println(s.fullName) }

在您的情况下,如果您将订单从ScrumptiousTypes with YummyTypes更改为YummyTypes with ScrumptiousTypes,则错误将显示在方法yumminess上。

@余杰水的替代方法是扩展内部类,如

case class Node() extends super[ScrumptiousTypes].Node with super[YummyTypes].Node 

答案 2 :(得分:1)

这不是一个答案。它只是对规范的引用和解释,它们太长而不能轻易地放入注释中(由johny's answer提示)。我正在解释我的解释,以便你可以找到我错误的地方。也许这将导致一种解释或一种方法,将特征中的特征扩展链接起来(或者在不太可能发生的情况下,我的解释证明是正确的。)

Scala规范中的相关段落

  

§6.5 This and Super:引用超级 m 静态引用方法或类型 m 中的最不合适的超类型包含引用的最里面的模板。它评估该模板的实际超类型中的成员 m',该成员等于 m 或覆盖 m。

最大的问题是:规范中super.Node YummyTypes所指的内容是什么?为了找到答案,我们需要知道上面使用的特定规范术语的定义:

  

§5.1 Templates模板定义特征或类对象或单个对象的类型签名,行为和初始状态。

因此,模板就是我们通常所说的对象,类或特征定义。

  

§5.4 Traits trait 是一个类,可以作为mixin添加到其他类中。 ...模板的最不合适的超类型是由其所有父类类型组成的类类型或复合类型。

     

§5.1.2 Class Linearization:让 C 成为一个模板 C 1 with ... with C <子> 名词 C L C )的线性化定义如下:   

L C )= C,L C < em> n )+⃗... +⃗ L C 1 )   

这里+⃗表示连接,其中右操作数的元素替换左操作数的相同元素。

我认为线性化是一个类序列,您可以从定义的类开始,然后从右到左阅读带类型的。当线性化中的两个类定义具有相同名称的成员或类型(“元素”)时,首先出现的类“获胜”。

因此,Graph的线性化应为GraphYummyTypesScrumptiousTypesNodeTypes,其次是Any等标准内容。实际上,当我像这样修改Graph时,这已得到证实:

  object Graph extends ScrumptiousTypes with YummyTypes {
    case class Node() extends super.Node { /* … */ }

    typeOf[Graph.type].baseClasses foreach { s => println(s.fullName) }
  }

产生:

Graph
YummyTypes
ScrumptiousTypes
NodeTypes
java.lang.Object
scala.Any
  

§5.4 Traits:假设特征 D 定义 C 类型的实例 x 的某些方面(即 D C 的基类。然后 x D 实际超类型是由 L 中的所有基类组成的复合类型(<成功 D的em> C 。实际的超类型给出了解析特征中超级引用的上下文。请注意,实际的超类型取决于在mixin组合中添加特征的类型;在定义特征时,它并不是静态知道的。

我认为这意味着混合特性的“实际”最不合适的超类型由特征被混合到的实际对象的类型决定(在我的例子中是Graph),不一定特征定义明确扩展的超类型(在我的例子中为NodeTypes)。

结论

因此,似乎YummyTypesGraph的实际超类型应为ScrumptiousTypes。因此,YummyTypes.NodeGraph的实际超类型应为ScrumptiousTypes.Node

但是,请将此行添加到Graph

typeOf[Node].baseClasses foreach { s => println(s.fullName) }

产生

Graph.Node
scala.Serializable
java.io.Serializable
scala.Product
scala.Equals
YummyTypes.Node
NodeTypes.Node
java.lang.Object
scala.Any
缺少

ScrumptiousTypes.Node。显然,在YummyTypes内,super.Node并未引用Node中实际最不合适的超类型中的YummyTypes

但是,如果我添加:

abstract override def text = super.text + " ScrumptiousTypes" // in ScrumptiousTypes 
abstract override def text = super.text + " YummyTypes"       // in YummyTypes

text中打印Graph会产生:

 ScrumptiousTypes YummyTypes

证明YummyTypes中的Graphsuper.text 确实是指ScrumptiousTypes.text