rapidninja rapidninja - 2 days ago 6
Scala Question

Defining a scala trait def that returns a concrete sub type instance

I am attempting to define a base trait used for a simple CRUD system. However, I need the base trait to support a "copyWithId" def (since the Scala case class magic for copy is not available). The best approach I have found so far is:

trait Identifiable[ID, T] {
def id: Option[ID]
def copyWithId(id: Option[ID]): T
}

case class TestNamedIdentity(id: Option[Int], name: String)
extends Identifiable[Int, TestNamedIdentity] {

def copyWithId(id: Option[Int]): TestNamedIdentity = {
copy(id = id)
}
}


This works but looks a bit clunky because it requires all the concrete instances to extend with ID and Self. I'd like to write something like the following:

trait Identifiable[ID] {
this:X =>
def copyWithId(id: Option[ID]): X
def id: Option[ID]
}


Using some form of self-reference to the concrete class. Any way to make this work?

Update: With use case below

The idea is to use it in code like

abstract class SomeClass[A <: IdentifiableEntity[ID], ID] {
def someFunc2: Option[ID]
def someFunc(item: A): A = {
item.copyWithId(someFunc2)
}
}


Using the solution from @jwvh requires "asInstanceOf" casting after the call. Which for my use case works, but was hoping for a more elegant solution.

item.copyWithId(someFunc2).asInstanceOf[A]

Answer

This appears to work.

trait Identifiable[ID] {
  def id: Option[ID]
  def copyWithId(id: Option[ID]): Identifiable[ID]
}

Then the case class is simplified.

case class TestNamedIdentity(id: Option[Int], name: String
                            ) extends Identifiable[Int] {
  def copyWithId(id: Option[Int]): TestNamedIdentity = copy(id = id)
}
Comments