在多个级别中扩展traits时继承不可变返回类型

时间:2014-02-06 03:43:21

标签: scala traits

我有以下特质层次结构。 TraitA是根特征,并且假设我希望我的数据结构是不可变的,函数commonUpdateFunction()具有泛型返回类型。我不确定这是不是最好的方法。我还有两个扩展它的特性,增加了两个其他功能。有些类扩展了一个类,有些类扩展了另一个类,但是有些类需要扩展它们。

但是,我现在遇到一个问题,因为那个泛型类型的东西我得到了非法的继承,实际上我只是在将数据结构更新为新的时才得到正确的类型。

此外,由于此泛型类型,我似乎无法将TraitA作为参数传递。

trait TraitA[T <: TraitA[T]]
{
   self : T =>

   def commonUpdateFunction() : T
}

trait TraitB extends TraitA[TraitB]
{
   def someFunctionB() : Integer = { /// some code }
}

trait TraitC extends TraitA[TraitC]
{
   def someFunctionC() : Unit = { /// some code } 
}

class ClassB extends TraitB 
{
   def commonUpdateFunction() : ClassB = { /// some code } 
}

class ClassC extends TraitC
{
   def commonUpdateFunction() : ClassC = { /// some code }
} 

class ClassA extends TraitB with TraitC  //**this causes illegal inheritance**
{
   def commonUpdateFunction() : ClassA = { /// some code }
} 

在具有正确类型的数据结构的不可变更新的同时,在2个特征上实现此继承的正确方法是什么?

1 个答案:

答案 0 :(得分:3)

类型参数不是你的问题,问题是ClassA尝试混合三个commonFunction()副本,这些副本只有返回类型不同:

class ClassA extends TraitB with TraitC {
  def commonFunction() : ClassA = { /// some code }
  def commonFunction() : ClassB = { /// some code } 
  def commonFunction() : ClassC = { /// some code } 
} 

虽然JVM 允许允许在返回类型上重载,但在编译时不允许这样做 - 混淆的可能性太大(特别是如果涉及类型推断)。

解决方案通常是使用f-bounded多态(就像你对commonUpdateFunction()所做的那样),但是鉴于所有commonFunction()定义都是具体的,这里不可能显示如何做到这一点。

看到更多“真实”的代码会有很多帮助!


更新:根据评论中的新信息。

使用抽象类型成员,您可能会发现生活更轻松,而不是类型参数。使用Repr(用于“Repr”表示)是一种常见的约定,并在集合lib中使用;确保此抽象类型成员具有绑定!

将其他常见属性粘贴在此处:

trait Employee {
  type Repr <: Employee

  def name : String
  def id   : Int

  def withName(name: String) : Repr
  def withId(id: Int) : Repr
}

子特征遵循类似的模式。没有必要重新声明保留其签名的其他抽象成员。您可以在此处细化类型时介绍其他成员。

trait ManagingEmployee extends Employee {
  type Repr <: ManagingEmployee
  def numberOfReports: Int
  def withNumberOfReports(x: Int) : Repr
}

trait SkilledEmployee extends Employee {
  type Repr <: SkilledEmployee 
  def skill: String
}

现在使我们的类型树的叶节点具体化。案例类在这里运作良好,但遗憾的是会有一些重复(宏可能会有所帮助,但这是一个不同的问题)。

请注意类别参数如何使nameid具体化,Repr类型通过=符号显式化,抽象方法必须是在每个叶类中明确重新定义:

case class HrManager(
  name            : String,
  id              : Int,
  numberOfReports : Int
) extends ManagingEmployee {
  type Repr = HrManager
  def withName(name: String) = this.copy(name = name)
  def withId(id: Int) = this.copy(id = id)
  def withNumberOfReports(x: Int) = this.copy(numberOfReports = id)
}

case class Technician(name: String, id: Int) extends SkilledEmployee {
  type Repr = Technician
  def withName(name: String) = this.copy(name = name)
  def withId(id: Int) = this.copy(id = id)
  val skill = "programming"
} 

case class TechnicalManager(
  name            : String,
  id              : Int,
  numberOfReports : Int
) extends SkilledEmployee with ManagingEmployee {
  type Repr = TechnicalManager 
  def withName(name: String) = this.copy(name = name)
  def withId(id: Int) = this.copy(id = id)
  def withNumberOfReports(x: Int) = this.copy(numberOfReports = id)
  val skill = "software architecture"
}