Scala: Good way to extending "static" trait

Viewed 72

I have a trait and some classes extending it. The problem is that all method in the trait should be implemented statically, so I put them in the corresponding companion objects. I wrote something like:

A.scala

trait A

C1.scala

class C1 extends A {
  override def F = C1.F
}
object C1 extends A {
  def F: String = ???
}

...

As the method F has to be implemented twice, I added a corresponding companion trait.

Now I have something like:

A.scala

trait A

AC.scala

trait AC {
 def F: String
}

C1.scala

class C1 extends A {
}
object C1 extends AC {
  def F: String = ???
}

C2.scala

class C2 extends A {
}
object C2 extends AC {
  def F: String = ???
}

...

I feel a little strange because there is nothing but an empty trait A in A.scala. I wonder if there is any better way to organize the codes.

1 Answers

I believe that you should first consider whether you really want any methods in the trait A, or whether you could move it into a typeclass (you have a companion object already anyway). The result will look like ordinary method invocation, but all such invocations will, per definition, go through the typeclass instance.


That being said, if you prefer "extending"...

If there are multiple methods in trait A, and they all should be implemented in the companion object, you could use the following classic OOP-ish pattern from Scala collections:

trait A[C <: A[C]] {
  self: C =>
  def companion: ACompanion[C]           // one abstract `def`
  def f(): String = companion.f()        // implemented once
}

trait ACompanion[C <: A[C]] {
  def f(): String
}

class C1 extends A[C1] {
  val companion = C1                     // one line per class `C_N`
}

object C1 extends ACompanion[C1] {
  def f() = "that's a C1"                // all actual implementation in companion
}

println(new C1().f())

The idea is that in the trait A, the methods that rely on the companion (like f), are delegated to the companion (thus, they are defined once as companion.f()).

In the implementing classes, you only have to specify what the companion is (one line per subclass).

This can reduce amount of boilerplate if there are multiple classes like C1 ... C_N, and more methods like f.

Related