Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Automatic Generation of Delegate Methods with Macro Annotations

DZone's Guide to

Automatic Generation of Delegate Methods with Macro Annotations

· Java Zone
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

Macro Annotations are a new type of macros, which most probably will be available in the upcoming Scala 2.11 release. However, thanks to the recently released Macro Paradise Scala 2.10 compiler plugin, with an extra option in the compiler/SBT settings, you can use them today, while still using a stable Scala version at runtime.

One of the Macro Annotations use-cases mentioned in the manual is compile-time AOP. I decided to try implementing something similar, but a bit simpler for a start: automatic generation of delegate methods (decorator pattern/proxy pattern). In fact, some years ago there was a similar effort using a compiler plugin (autoproxy plugin). As an additional motivation, Łukasz recently asked on our technical room if Scala has this exact functionality – instead of saying “No”, I should have said “Not yet” ;).

The results of the POC are available on GitHub, in the scala-macro-aop repository:https://github.com/adamw/scala-macro-aop. If you have SBT, you can play with the implementation just by invoking run from the SBT console.

How does it work? Let’s say we have an interface Foo with three methods (with very original names:method1method2 and method3), each taking some parameters. We have a default implementation:

trait Foo {
   def method1(param1: String): Int
   def method2(p1: Int, p2: Long): Float
   def method3(): String
}
 
class FooImpl extends Foo {
   def method1(param1: String) = param1.length
   def method2(p1: Int, p2: Long) = p1 + p2
   def method3() = "Hello World!"
}

Now we would like to create a wrapper for a Foo instance, which would delegate all method calls to the given instance, unless the method is defined in the wrapper.

The traditional solution is to create a delegate for each method by hand, e.g.:

class FooWrapper(wrapped: Foo) extends Foo {
   def method1(param1: String) = wrapped.method1(param1)
   def method2(p1: Int, p2: Long) = wrapped.method2(p1, p2)
   def method3() = wrapped.method3()
}

But that’s a lot of work. Using the @delegate macro, the delegate methods will now be automatically generated at compile time! That is, the wrapper now becomes:

class FooWrapper(@delegate wrapped: Foo) extends Foo {
   // method1, method2 and method3 are generated at compile time
   // and delegate to the annotated parameter
}

What if we want to implement some methods? The macro will generate only the missing ones:

class FooWrapper(@delegate wrapped: Foo) extends Foo {
   def method2(p1: Int, p2: Long) = p1 - p1
   // only method1 and method3 are generated
}

As the implementation is just a POC, it will only work in simple cases, that is for methods with a single parameter list, without type parameters and when the method is not overloaded. Plus the code of the macro is, let’s say, “not yet polished” ;).

As mentioned before, the code is on GitHub: https://github.com/adamw/scala-macro-aop, available under the Apache2 license.

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:

Published at DZone with permission of Adam Warski, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}