Tuesday, November 22, 2011

What’s So Special About Xtend’s Extension Methods?

You should read this, if you are :

  • interested in new programming language concepts
  • understood how great dependency injection is
  • want to learn about some uniqueness in Eclipse Xtend

Extension Methods

Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. In C# extension methods are a special kind of static method, but they are called as if they were instance methods on the extended type. (Wikipedia November 2011)

In Xtend you could, for instance, add a new method to java.util.List like this:

def static <T> T head(List<T> list) {

  for (element : list)

    return element

  return null

}

Which then can be used as an extension method on any instance of Iterable<T>:

newArrayList(“foo”,”bar”).head()

Using the prefix notation is much closer to how we think, read, and write. From left to right that is. Also the IDE can now provide useful proposals because the receiver is known. The readability gets even better if you use chained method invocations. For instance, let's assume you want to filter the list before obtaining the head. Using the regular infix syntax known from Java the code would like like that :

head(filter(newArrayList(“foo”,”bar”) , [e | e != “foo”]))

But reading inside out doesn’t seem to be the best way to understand what’s going on. So better use extension methods:

newArrayList(“foo”,”bar”).filter(e | e != “foo”).head

Much better, isn't it?


Static Methods Are Bad!

But wait, don’t extension methods advocate the heavy use of static methods? And isn’t that bad coding style?

Yes, often it is.

With static methods you’ll bind your code not only to the signature of a certain method but to the actual implementation. There’s no way (aside from byte code manipulation) to change the implementation without touching the client code. That’s probably not a big deal with the kind of extension methods I just showed. But what if you want to have DAO-like methods available on your domain model types? Accessing them in a static way seems totally uncool, since we wouldn’t be able to run the code with mocks or exchange the database layer easily or just fix or change something later on. That's why static methods are a bad choice most of the time.

Typically you would want to use a dependency injection container to have such services injected. This is where Xtend’s extension methods are different from the one you find in C#.

Xtend allows to use methods from local fields as extension methods.

Let’s assume we have the following Java interface:

interface SaveSupport {

  void save(Entity entity);

}

And given we have some domain model type Person which implements Entity, you can write the following code:

class Controller {


  @Inject extension SaveSupport


  def testStuff() {

    val p = new Person()

    p.name = “Fred Flintstone”

    p.save // translated to ‘this._saveSupport.save(p)’

  }

}

That’s a big deal, because now you can use an object oriented coding style but keep your domain model free from layer specific code at the same time! And everything is easily testable and can be run in different scenarios using different DI configuration. There are other unique language features in Xtend, like the template expression, but the combination of extension methods and a good dependency injection framework (Guice) really changes how you structure your software system and reason about it.

3 comments:

  1. Hey Sven,

    Just a little correction.

    Extension functions don't have to be static in Kotlin. They may be members of classes:

    class Foo() {
    fun Bar.extension() {
    // ...
    }
    }

    ReplyDelete
  2. Ah ok, cool.

    So i can write myBar.extension if I'm on the context of class Foo (i.e. in the class or a subclass)?

    Is there a way to use that function as an extension function by having an instance of Foo in the scope (other than 'this')?

    ReplyDelete
  3. Hi,

    that's interesting. How do you deal with cases where several injectees would provide a matching extension method?

    --Gunnar

    ReplyDelete