Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Scala is there a way to reference the Companion Object from within an instance of a Case Class?

In my specific case I have a (growing) library of case classes with a base trait (TKModel)

Then I have an abstract class (TKModelFactory[T <: TKModel]) which is extended by all companion objects.

So my companion objects all inherently know the type ('T') of "answers" they need to provide as well as the type of objects they "normally" accept for commonly implemented methods. (If I get lazy and cut and paste chunks of code to search and destroy this save my bacon a lot!) I do see warnings on the Internet at large however that any form of CompanionObject.method(caseClassInstance: CaseClass) is rife with "code smell" however. Not sure if they actually apply to Scala or not?

There does not however seem to be any way to declare anything in the abstract case class (TKModel) that would refer to (at runtime) the proper companion object for a particular instance of a case class. This results in my having to write (and edit) a few method calls that I want standard in each and every case class.

case class Track(id: Long, name: String, statusID: Long) extends TKModel

object Track extends TKModelFactory[Track]

How would I write something in TKModel such that new Track(1, "x", 1).someMethod() could actually call Track.objectMethod()

Yes I can write val CO = MyCompanionObject along with something like implicit val CO: ??? in the TKModel abstract class and make all the calls hang off of that value. Trying to find any incantation that makes the compiler happy for that however seems to be mission impossible. And since I can't declare that I can't reference it in any placeholder methods in the abstract class either.

Is there a more elegant way to simply get a reference to a case classes companion object?

My specific question, as the above has been asked before (but not yet answered it seems), is there a way to handle the inheritance of both the companion object and the case classes and find the reference such that I can code common method calls in the abstract class?

Or is there a completely different and better model?

like image 961
Techmag Avatar asked Apr 28 '15 14:04

Techmag


Video Answer


2 Answers

If you change TKModel a bit, you can do

abstract class TKModel[T <: TKModel] {
  ...
  def companion: TKModelFactory[T]
  def someMethod() = companion.objectMethod()
}

case class Track(id: Long, name: String, statusID: Long) extends TKModel[Track] {
  def companion = Track
}
object Track extends TKModelFactory[Track] {
  def objectMethod() = ...
}

This way you do need to implement companion in each class. You can avoid this by implementing companion using reflection, something like (untested)

lazy val companion: TKModelFactory[T] = {
  Class.forName(getClass.getName + "$").getField("MODULE$").
    get(null).asInstanceOf[TKModelFactory[T]]
}

val is to avoid repeated reflection calls.

like image 50
Alexey Romanov Avatar answered Nov 22 '22 11:11

Alexey Romanov


A companion object does not have access to the instance, but there is no reason the case class can't have a method that calls the companion object.

case class Data(value: Int) {
  def add(data: Data) = Data.add(this,data)
}

object Data {
  def add(d1: Data, d2: Data): Data =  Data(d1.value + d2.value)
}
like image 31
David Holbrook Avatar answered Nov 22 '22 11:11

David Holbrook