不可变类的方法继承 [英] Method inheritance in immutable classes

查看:146
本文介绍了不可变类的方法继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我绊倒了我希望的一些基本问题。可能是因为我是新来的Scala,可能我还是缺少一些重要的概念。



我正在尝试以FP方式编程,而不是数据类需要有一个可变状态是不可变的,用一些转换方法来创建新的对象来更新它们,如果需要的话。但是,当我有特征和一般的继承到位时,我正在努力维护这种方法的返回类型。我希望尽可能避免混乱的类型或类似的东西,因为这仍然是我的学习经历。



在这里看到这个例子,我有一个不可改变的类扩展了一些特质。 更新方法旨在更改数据,调用实际类的某些方法(在trait中是抽象的),并将同一类的新实例更新为新数据。人们可以将其粗略地映射到模板模式。

  trait MyTrait 
{
val someDataVal:Integer;

def update(newDataVal):MyTrait = {
//某些逻辑在这里发生,这是常见的

abstractUpdate(newDataVal)
}

//某些逻辑发生,具体到实现类
def abstractUpdate(newDataVal:Integer):MyTrait
}


类MyClass(dataVal:Integer)扩展MyTrait
{
覆盖val someDataVal = dataVal

def abstractUpdate(newDataVal:Integer):MyClass = {
//某些类特定逻辑在这里........

MyClass(newDataVal)
}

def someOtherFunction():Integer = {
// some这里的逻辑.....
}
}

我显然不会希望将 update()复制并粘贴到 MyClass 中,我希望它保持特性,以便我可以使用它通过任何类扩展它。但是,如果我尝试调用它,我得到的是一个类型为 MyTrait 的对象,所以我不能调用 someOtherFunction()在它上。



什么是正确的Scala方法来实现这种OO重用,并且仍然使我的代码清洁?



更新



请注意我把 //一些逻辑发生在这里,这意味着我可能有一些代码,我想集中在特征中,而不是复制和粘贴在每个具体的类中扩展它。这只是一个说明问题的骨架。感谢您的时间。



更新



基于提供的答案的代码示例通过小麦。问题是与返回这个

  trait MyTrait [T< ;:MyTrait [T]] {
def update(newValue:Int):T = {
if(newValue == 0)
return this; //这创建一个类型不匹配
else
concreteUpdate(newValue)
}

def concreteUpdate(value:Int):T
}

class MyClass(value:Int)extends MyTrait [MyClass]
{
override def concreteUpdate(value:Int)= new MyClass(value)
}


解决方案

我之前已经回答过一个类似的问题,而@GaborBakos的评论是现场上。如果您希望能够使用地图方法( TraverseableLike )之类的内容,您需要执行以下操作:

  trait MyTrait [T< ;: MyTrait [T]] {
def update (newValue:Int):T
}

这基本上是一个类型定义,依赖于本身!因此,更新的返回类型是 T 。然后:

  class MyClass(value:Int)extends MyTrait [MyClass] {
def update(newValue:Int) = new MyClass(newValue)
}

T MyClass



侧注: / p>

不要在特征中放入 val 。相反,使它成为一个 def 。这样,你不会遇到任何想要扩展你的类的初始化排序问题。如果你不遵循这个建议,那么你可以遇到一个非常 null 字段被视为一个 null


I am stumbling on something that I hope is a bit of a basic issue. Probably its because I am new to Scala, and probably I am still missing some important concepts.

I am trying to program in an FP fashion, and data classes which do not need to have a mutable state are immutable, with some transformation methods to create new objects to update them if needed. However, I am struggling when it comes to maintaining the return types of this method when I have traits and general inheritance in place. I wish to avoid messy type casts or things like that as much as possible, because this is still a learning experience for me.

See this example here, where I have an immutable class extending some trait. The update method is intended to change the data, call some method of the actual class (which is abstract in the trait) and return a new instance of the same class updated to the new data. One could map this roughly to the Template pattern.

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 .....
   }
 }

I obviously do not wish to copy and paste update() into MyClass I want it to stay in the trait so that I can use it by any class extending it. However if I try to call it, what I get is an object of type MyTrait, and so I cannot call someOtherFunction() on it.

What is the right Scala approach to achieve this kind of OO reuse and still have my code clean?

UPDATE

Please take note of the places where I put //some logic takes place here, that means I might have some code there which I want to be centralised in the trait and not copied and pasted in each concrete class extending it. This is just a skeleton to illustrate the problem. Thanks for your time.

UPDATE

Code example based on the answer provided by wheaties. The issue is with the 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)
}

解决方案

I've answered a similar question before and the comment by @GaborBakos is spot on. If you'd like to be able to do similar things to what you might find with the map method of TraverseableLike then you need to do the following:

trait MyTrait[T <: MyTrait[T]]{
  def update(newValue: Int): T
}

which basically is a type definition which depends on itself! Hence, the return type of update is T. Then:

class MyClass(value: Int) extends MyTrait[MyClass]{
  def update(newValue: Int) = new MyClass(newValue)
}

which should work since T is MyClass.

Side Note:

Don't put val in a trait. Instead, make it a def. That way, you don't run into initialization ordering issues with anyone that wants to extend your class. If you don't follow this advice, then you can run into situations where a very NOT null field is being treated as a null.

这篇关于不可变类的方法继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆