При расширении черты в пределах черты, что означает "супер"?
Я хочу расширить черту в пределах признака, например:
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
- и почему?
Если вам интересно, почему я это делаю, так что я могу смешивать изменения сразу по нескольким признакам, поэтому унаследованные черты взаимодействуют, как объясняется в этом вопросе. В конечном классе Node (и других классах, с которыми он работает) я не хочу явно выделяться из каждого признака Node: я хочу смешивать из одной "вещи" (что бы это ни было) и получить все взаимно согласованные изменения, сделанные в Node, и другие черты, все в комплекте. Или, если один признак определяет кучу расширений до Node, расширение от ScrumptiousTypes должно сделать все Node -extensions содержать член scrumptiousness
, не указывая всех Node -extensions: trait Hypernode extends ScrumptiousTypes.Node
, trait ZealousNode extends ScrumptiousTypes.Node
и т.д.
Ответы
Ответ 1
тип использования также устраняет проблему
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
}
}
Ответ 2
Это связано с линеаризацией класса. См. Spec.
Объяснение
Пусть C - класс с шаблоном C1 with ... with Cn
. Тогда линеаризация представляет собой конкатенацию элементов из Cn в C1, заменяя все одинаковые элементы налево. Здесь элементы включают var, val, def, traits, object.
Если вы хотите увидеть порядок линеаризации, используйте
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
Ответ 3
Это не должно быть ответом. Это просто цитаты из и интерпретации спецификации, которые слишком долго подходят для чтения в комментариях (вызвано johny answer). Я излагаю свои интерпретации, чтобы вы могли определить, где я ошибся. Может быть, это приведет к объяснению или к способу цепочки расширений черт в чертах (или в отчете об ошибке в маловероятном случае, когда моя интерпретация окажется правильной).
Соответствующие проходы из Scala spec
§6.5 Это и супер: ссылка супер.m ссылается статически на метод или тип m в наименее правильный супертип самого внутреннего шаблона, содержащего ссылку. Он оценивает член m 'в фактическом супертипе этого шаблона, который равен m или который переопределяет m.
Большой вопрос: что говорит спецификация super.Node
внутри YummyTypes
? Чтобы узнать, нам нужно знать определения спецификационных терминов, используемых выше:
§5.1 Шаблоны: шаблон определяет сигнатуру типа, поведение и начальное состояние признака или класса объектов или одного объекта.
Итак, шаблон - это то, что мы обычно называем определением объекта, класса или определения.
§5.4. Черты: признак - это класс, который должен быть добавлен в какой-то другой класс в качестве микса.... Наименее правильным супертипом шаблона является тип класса или составной тип, состоящий из всех его типов родительского класса.
§5.1.2 Линеаризация классов: Пусть C - класс с шаблоном C 1with ... with
C <суб > псуб > . Линеаризация C, L (C) определяется следующим образом:
L (C) = C, L (C n) + ⃗... + ⃗ L (C 1)
Здесь + ⃗ обозначает конкатенацию, где элементы правого операнда заменяют одинаковые элементы левого операнда.
Я считаю, что линеаризация - это последовательность классов, которую вы получаете, начиная с определяемого класса и затем читая типы с справа налево. Когда два класса в линеаризации определяют член или тип с тем же именем ( "элемент" ), класс, который на первом месте "выигрывает".
Итак, линеаризация Graph
должна быть Graph
, YummyTypes
, ScrumptiousTypes
, NodeTypes
, за которой следуют стандартные вещи, такие как 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 Черты: предположим, что признак D определяет некоторый аспект экземпляра x типа C (т.е. D является базовым классом C). Тогда фактический супертип D в x является составным типом, состоящим из всех базовых классов в L (C), которые достигают D. Фактический супертип дает контекст для разрешения суперссылки в признаке. Обратите внимание, что фактический супертип зависит от типа, к которому добавляется признак в композиции смеси; он не статически известен во время определения признака.
Я считаю, что "фактический" наименее правильный супертип смешанного признака определяется типом фактического объекта, в котором этот признак смешивается (Graph
в моем примере), не обязательно супертип что определение признака явно расширяет (NodeTypes
в моем примере).
Заключение
Итак, кажется, что фактический супертип YummyTypes
в Graph
должен быть ScrumptiousTypes
. Итак, фактический супертип YummyTypes.Node
в Graph
должен быть 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
in YummyTypes
"фактическому наименее правильному супертипу.
Однако, если я добавлю:
abstract override def text = super.text + " ScrumptiousTypes" // in ScrumptiousTypes
abstract override def text = super.text + " YummyTypes" // in YummyTypes
печать text
в Graph
вызывает:
ScrumptiousTypes YummyTypes
демонстрируя, что внутри YummyTypes
в Graph
, super.text
ссылается на ScrumptiousTypes.text
!