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.

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

Comments

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.

1 Comment

Thanks! I will have a look at macros as soon as we can move to Scala 2.10
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.

1 Comment

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!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.