hipjim
hipjim

Reputation: 128

Way to enhance a class with function delegation

I have the following classes in Scala:

class A {
    def doSomething() = ???

    def doOtherThing() = ???
}

class B {
    val a: A

    // need to enhance the class with both two functions doSomething() and doOtherThing() that delegates to A
    // def doSomething() = a.toDomething()
    // def doOtherThing() = a.doOtherThing()
}

I need a way to enhance at compile time class B with the same function signatures as A that simply delegate to A when invoked on B.

Is there a nice way to do this in Scala?

Thank you.

Upvotes: 6

Views: 385

Answers (4)

cmhteixeira
cmhteixeira

Reputation: 967

There is this macro delegate-macro which might just be what you are looking for. Its objective is to automatically implement the delegate/proxy pattern, so in your example your class B must extend class A.

It is cross compiled against 2.11, 2.12, and 2.13. For 2.11 and 2.12 you have to use the macro paradise compile plugin to make it work. For 2.13, you need to use flag -Ymacro-annotations instead.

Use it like this:

trait Connection {
  def method1(a: String): String
  def method2(a: String): String
  // 96 other abstract methods
  def method100(a: String): String
}

@Delegate
class MyConnection(delegatee: Connection) extends Connection {
  def method10(a: String): String = "Only method I want to implement manually"
}

// The source code above would be equivalent, after the macro expansion, to the code below
class MyConnection(delegatee: Connection) extends Connection {
  def method1(a: String): String = delegatee.method1(a)
  def method2(a: String): String = delegatee.method2(a)
  def method10(a: String): String = "Only method I need to implement manually"
  // 96 other methods that are proxied to the dependency delegatee
  def method100(a: String): String = delegatee.method100(a)
}

It should work in most scenarios, including when type parameters and multiple argument lists are involved.

Disclaimer: I am the creator of the macro.

Upvotes: 0

Mario Galic
Mario Galic

Reputation: 48430

Implicit conversion could be used for delegation like so

object Hello extends App {
  class A {
    def doSomething() = "A.doSomething"
    def doOtherThing() = "A.doOtherThing"
  }

  class B {
    val a: A = new A
  }

  implicit def delegateToA(b: B): A = b.a
  val b = new B
  b.doSomething() // A.doSomething
}

Upvotes: 4

Alexey Romanov
Alexey Romanov

Reputation: 170899

In Dotty (and in future Scala 3), it's now available simply as

class B {
    val a: A

    export a
}

Or export a.{doSomething, doOtherThing}.

For Scala 2, there is unfortunately no built-in solution. As Tim says, you can make one, but you need to decide how much effort you are willing to spend and what exactly to support.

Upvotes: 9

Tim
Tim

Reputation: 27421

You can avoid repeating the function signatures by making an alias for each function:

val doSomething = a.doSomething _
val doOtherthing = a.doOtherThing _

However these are now function values rather than methods, which may or may not be relevant depending on usage.

It might be possible to use a trait or a macro-based solution, but that depends on the details of why delegation is being used.

Upvotes: 4

Related Questions