Scala 在多个级别扩展traits时不可变返回类型的继承
我有以下特征层次结构。TraitA是根特征,如果我希望我的数据结构是不可变的,那么commonUpdateFunction函数有一个通用的返回类型。我不确定这是不是最好的办法。我还有两个扩展它的特性,添加了另外两个函数。有些类扩展一个类,有些类扩展另一个类,但有些类需要同时扩展这两个类 然而,我现在遇到了一个问题,由于泛型类型的原因,我获得了非法继承,而实际上,我这样做只是为了在将数据结构更新为新结构时获得正确的类型 此外,由于这个泛型类型,我似乎无法将TraitA作为参数传递Scala 在多个级别扩展traits时不可变返回类型的继承,scala,traits,Scala,Traits,我有以下特征层次结构。TraitA是根特征,如果我希望我的数据结构是不可变的,那么commonUpdateFunction函数有一个通用的返回类型。我不确定这是不是最好的办法。我还有两个扩展它的特性,添加了另外两个函数。有些类扩展一个类,有些类扩展另一个类,但有些类需要同时扩展这两个类 然而,我现在遇到了一个问题,由于泛型类型的原因,我获得了非法继承,而实际上,我这样做只是为了在将数据结构更新为新结构时获得正确的类型 此外,由于这个泛型类型,我似乎无法将TraitA作为参数传递 trait Tr
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个特征的继承的正确方法是什么 这里类型参数不是您的问题,问题是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-有界多态性,就像您在commonUpdateFunction中所做的那样,但鉴于您所有的commonFunction定义都是具体的,因此不可能在这里说明如何做到这一点
看到更多真实的代码会有很大帮助
更新:基于评论中的新信息
您可能会发现使用抽象类型成员比使用类型参数更容易。使用Repr表示是一种常见的约定,在集合库中使用;确保此抽象类型成员具有绑定
将其他常见属性也粘贴在此处:
trait Employee {
type Repr <: Employee
def name : String
def id : Int
def withName(name: String) : Repr
def withId(id: Int) : Repr
}
类型参数在这里不是您的问题,问题是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-有界多态性,就像您在commonUpdateFunction中所做的那样,但鉴于您所有的commonFunction定义都是具体的,因此不可能在这里说明如何做到这一点
看到更多真实的代码会有很大帮助
更新:基于评论中的新信息
您可能会发现使用抽象类型成员比使用类型参数更容易。使用Repr表示是一种常见的约定,在集合库中使用;确保此抽象类型成员具有绑定
将其他常见属性也粘贴在此处:
trait Employee {
type Repr <: Employee
def name : String
def id : Int
def withName(name: String) : Repr
def withId(id: Int) : Repr
}
我是Scala的初学者,所以我可能遗漏了一些重要的设计原则。我正在努力弄清楚的是,如何在保持数据对象不变的同时实现传统的OO多态性。这将是一个典型的例子:Employee作为一个抽象类,它有名称和employeeId,还有一些类扩展它,比如Technician和Manager。更新名称或employeeId的方法必须创建另一个相同类型的实例(如果它是不可变的)。另一方面,系统的其余部分有一些方法,这些方法可以接受employees。在这种情况下,您可以将Employee作为trait,并为technology和Manager创建case类子类型。像对commonUpdateFunction那样声明withName和withId,然后根据copy方法在case类中定义。我想知道您是否希望commonFunction和commonUpdateFunction在您的示例中是相同的东西;因为现在他们非常不同,我错了,他们应该是一样的。固定的是的,我明白了,问题出现在其他地方,我只需要使用Employee作为一个完全无关类的方法的参数。它不在乎自己是什么类型的员工。现在我只是得到了一些错误,说它需要指定类型,我想是因为[T]的东西,它是用来确定不可变类的真正类型的。所以您声明了一个内部类型,它被具体类重写。。。好把戏!我会尝试一下。。。谢谢你千方百计的解释。这些概念对我们初学者来说有点难,即使我在Java方面很有经验,拥有不可变对象需要一个非常不同的视角。所以神奇的短语是它是一个抽象类型成员而不是一个类型参数,并且它的类型绑定为Employee的一个子类。Scala书中的编程有一整节是关于成员和参数的。我是Scala的初学者,所以我可能遗漏了一些重要的设计原则。我努力想弄明白的是如何实现传统的OO多态性
同时保持数据对象不变。这将是一个典型的例子:Employee作为一个抽象类,它有名称和employeeId,还有一些类扩展它,比如Technician和Manager。更新名称或employeeId的方法必须创建另一个相同类型的实例(如果它是不可变的)。另一方面,系统的其余部分有一些方法,这些方法可以接受employees。在这种情况下,您可以将Employee作为trait,并为technology和Manager创建case类子类型。像对commonUpdateFunction那样声明withName和withId,然后根据copy方法在case类中定义。我想知道您是否希望commonFunction和commonUpdateFunction在您的示例中是相同的东西;因为现在他们非常不同,我错了,他们应该是一样的。固定的是的,我明白了,问题出现在其他地方,我只需要使用Employee作为一个完全无关类的方法的参数。它不在乎自己是什么类型的员工。现在我只是得到了一些错误,说它需要指定类型,我想是因为[T]的东西,它是用来确定不可变类的真正类型的。所以您声明了一个内部类型,它被具体类重写。。。好把戏!我会尝试一下。。。谢谢你千方百计的解释。这些概念对我们初学者来说有点难,即使我在Java方面很有经验,拥有不可变对象需要一个非常不同的视角。所以神奇的短语是它是一个抽象类型成员而不是一个类型参数,并且它的类型绑定为Employee的一个子类。Scala书中的编程有一整节是关于成员和参数的。