2

I want to create a method that generates an implementation of a trait. For example:

trait Foo {
  def a
  def b(i:Int):String
}

object Processor {
  def exec(instance: AnyRef, method: String, params: AnyRef*) = {
    //whatever
  }
}

class Bar {
  def wrap[T] = {
    // Here create a new instance of the implementing class, i.e. if T is Foo,
    // generate a new FooImpl(this)
  }
}

I would like to dynamically generate the FooImpl class like so:

class FooImpl(val wrapped:AnyRef) extends Foo {
  def a = Processor.exec(wrapped, "a")
  def b(i:Int) = Processor.exec(wrapped, "b", i)
}

Manually implementing each of the traits is not something we would like (lots of boilerplate) so I'd like to be able to generate the Impl classes at compile time. I was thinking of annotating the classes and perhaps writing a compiler plugin, but perhaps there's an easier way? Any pointers will be appreciated.

Mario Camou
  • 2,303
  • 16
  • 28

3 Answers3

2

java.lang.reflect.Proxy could do something quite close to what you want :

import java.lang.reflect.{InvocationHandler, Method, Proxy}

class Bar {
  def wrap[T : ClassManifest] : T = { 
    val theClass = classManifest[T].erasure.asInstanceOf[Class[T]]
    theClass.cast(
      Proxy.newProxyInstance(
        theClass.getClassLoader(), 
        Array(theClass), 
        new InvocationHandler {
          def invoke(target: AnyRef, method: Method, params: Array[AnyRef])
            = Processor.exec(this, method.getName, params: _*)
        }))
    }
  }

With that, you have no need to generate FooImpl.

A limitation is that it will work only for trait where no methods are implemented. More precisely, if a method is implemented in the trait, calling it will still route to the processor, and ignore the implementation.

Didier Dupont
  • 29,398
  • 7
  • 71
  • 90
2

You can write a macro (macros are officially a part of Scala since 2.10.0-M3), something along the lines of Mixing in a trait dynamically. Unfortunately now I don't have time to compose an example for you, but feel free to ask questions on our mailing list at http://groups.google.com/group/scala-internals.

Community
  • 1
  • 1
Eugene Burmako
  • 13,028
  • 1
  • 46
  • 59
1

You can see three different ways to do this in ScalaMock.

ScalaMock 2 (the current release version, which supports Scala 2.8.x and 2.9.x) uses java.lang.reflect.Proxy to support dynamically typed mocks and a compiler plugin to generate statically typed mocks.

ScalaMock 3 (currently available as a preview release for Scala 2.10.x) uses macros to support statically typed mocks.

Assuming that you can use Scala 2.10.x, I would strongly recommend the macro-based approach over a compiler plugin. You can certainly make the compiler plugin work (as ScalaMock demonstrates) but it's not easy and macros are a dramatically superior approach.

Paul Butcher
  • 10,722
  • 3
  • 40
  • 44
  • I'll have a look at macros as soon as we can move to Scala 2.10, meanwhile I'll see about applying the Proxy approach. Thanks! – Mario Camou Aug 05 '12 at 18:20