我对某些事情感到磕磕绊绊,我希望这是一个基本问题。可能是因为我是Scala的新手,可能我仍然缺少一些重要的概念。
我正在尝试以FP方式编程,并且不需要具有可变状态的数据类是不可变的,使用一些转换方法来创建新对象以在需要时更新它们。但是,当我有特征和一般继承时,我在维护这个方法的返回类型时遇到了困难。我希望尽可能地避免混乱的类型演员或类似的事情,因为这对我来说仍然是一种学习经历。
在这里看到这个例子,我有一个不可变的类扩展了一些特征。 update
方法旨在更改数据,调用实际类的某些方法(在特征中是抽象的),并将更新的同一类的新实例返回到新数据。可以粗略地将其映射到模板模式。
trait MyTrait
{
val someDataVal : Integer;
def update(newDataVal) : MyTrait = {
//some logic takes place here, which is common
abstractUpdate(newDataVal)
}
//some logic takes place, specific to the implementation class
def abstractUpdate(newDataVal : Integer) : MyTrait
}
class MyClass(dataVal : Integer) extends MyTrait
{
override val someDataVal = dataVal
def abstractUpdate(newDataVal : Integer) : MyClass = {
//some class specific logic here ........
MyClass(newDataVal)
}
def someOtherFunction() : Integer = {
//some logic here .....
}
}
我显然不希望将update()
复制并粘贴到MyClass
我希望它保留在特征中,以便我可以在任何扩展它的类中使用它。但是,如果我尝试调用它,我得到的是MyTrait
类型的对象,因此我无法在其上调用someOtherFunction()
。
什么是正确的Scala方法来实现这种OO重用并仍然保持我的代码清洁?
更新
请注意我放置//some logic takes place here
的地方,这意味着我可能在那里有一些代码,我希望将其集中在特征中,而不是复制和粘贴到扩展它的每个具体类中。这只是解释问题的骨架。谢谢你的时间。
更新
基于小麦提供的答案的代码示例。问题出在return this
。
trait MyTrait[T <: MyTrait[T]]{
def update(newValue: Int): T = {
if (newValue == 0)
return this; //this creates a type mismatch
else
concreteUpdate(newValue)
}
def concreteUpdate(value : Int) : T
}
class MyClass(value: Int) extends MyTrait[MyClass]
{
override def concreteUpdate(value : Int) = new MyClass(value)
}
答案 0 :(得分:3)
之前我已经回答了类似的问题,@ GaborBakos发表了评论。如果您希望能够使用map
TraverseableLike
方法找到类似的内容,那么您需要执行以下操作:
trait MyTrait[T <: MyTrait[T]]{
def update(newValue: Int): T
}
基本上是一个依赖于自身的类型定义!因此,update
的返回类型为T
。然后:
class MyClass(value: Int) extends MyTrait[MyClass]{
def update(newValue: Int) = new MyClass(newValue)
}
应该有效,因为T
是MyClass
。
旁注:
不要将val
置于特质中。相反,将其设为def
。这样,您就不会遇到想要扩展您的类的任何人的初始化排序问题。如果您不遵循此建议,那么您可能会遇到非null
字段被视为null
的情况。