5

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.

hipjim
  • 118
  • 1
  • 6
  • 1
    Related, but very old and open-ended thread: https://stackoverflow.com/questions/3473309/proxies-delegates-in-scala – Thilo Apr 24 '19 at 09:06
  • 1
    Also https://stackoverflow.com/questions/27035808/scala-forward-or-delegate-methods-to-encapsulated-object – Thilo Apr 24 '19 at 09:07

4 Answers4

7

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.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
4

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.

Tim
  • 26,753
  • 2
  • 16
  • 29
  • You can make them methods about as easily as values ... `def doSomething = a.doSomething` etc. – Dima Apr 24 '19 at 12:00
4

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
}
Mario Galic
  • 47,285
  • 6
  • 56
  • 98
0

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.

cmhteixeira
  • 877
  • 11
  • 22