Method Inheritance in Immutable Classes

I stumble upon something that I hope is a bit of a basic problem. This is probably because I'm new to Scala, and I probably still miss some important concepts.

I am trying to program in FP mode, and data classes that do not have to have a mutable state are immutable, with some conversion methods for creating new objects to update them if necessary. However, I am afraid when it comes to preserving the return types of this method, when I have traits and common inheritance. I want to avoid messy tricks or similar things as little as possible, because for me it's still a learning experience.

See this example here, where I have an immutable class extending some features. The update method is intended to change data, call some method of the actual class (which is abstract in the attribute) and returns a new instance of the same class, updated to new data. You can match this with a pattern template.

 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 don't want to copy and paste update() into MyClass I want it to stay in the dash, so that I can use it with any class extending it. However, if I try to call it, I get an object of type MyTrait , and therefore I cannot call it someOtherFunction() .

What is the right Scala approach to achieve such reuse of OO and still keep my code clean?

UPDATE

Pay attention to the places where I put //some logic takes place here , this means that I may have some code that I want to centralize in the attribute, and not copy and paste into each specific class that extends it. It is just a skeleton to illustrate the problem. Thank you for your time.

UPDATE

Sample code based on a response provided by wheat. The problem is with 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) } 
+4
source share
1 answer

I answered a similar question before, and the comment from @GaborBakos is in place. If you want to do something similar using the map TraverseableLike method, then you must do the following:

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

which is basically a type definition that depends on itself! Therefore, 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 with T MyClass .

Side note:

Do not put val to hell. Instead, make it def . Thus, you do not face the problems of ordering initialization with anyone who wants to extend your class. If you do not follow this advice, you may encounter situations where the NOT null field is treated as null .

+3
source

All Articles