5 Things that make Xtend a great Language for Java Developers

26 Sep 2012

Xtend is a new programming language for the JVM. In contrast to other new languages for the JVM, Xtend stays very close to Java and uses the exact same type system. This makes Xtend really easy to learn if you already know Java. At the same time Xtend enriches Java with functionality greatly increasing developer productivity and making code easier to read. Over the last year I have been using Xtend on a regular basis and I can say that I really like it. Here is my completely subjective list of reasons why I think Xtend is a great language for Java developers.

1. It compiles to plain Java code

One of the best things about Xtend is that it compiles to readable Java code. No magic transformation into Java byte code that is impossible to understand for mere morals. Instead, it compiles to plain Java code that everyone can understand. You can see the generated code, when you create a new Xtend file in Eclipse. A new source folder xtend-gen will be created automatically containing the generated Java code. This is really helpful when learning Xtend as you can always see what the generated Java code will look like. Another advantage is that it is no problem to use Xtend classes from normal Java classes and vice versa, because Xtend uses the same type-system as Java. These things makes it easy to integrate Xtend into normal Java projects. The only dependency of Xtend is a small runtime library consisting mostly of Google Guava (which you should be using anyway). This makes Xtend also a great choice for developing Android or GWT applications. One important thing to note is, although the Xtend tooling is based on Eclipse, Xtend is completely independent of Eclipse. You can, for example, compile Xtend files with Maven.

2. Type Inference

If we look at the following Java code, we notice that the type information on the left hand side of the assignments is actually redundant, as the type is already defined by the right hand side of the assignment:

// variable declaration
String greeting = "Hello";
Map<String, Person> persons = new HashMap<String, Person>();

// with the diamond operator in Java 7
Map<String, Person> persons = new HashMap<>();

// with google collections
List<String> colors = newArrayList("red", "green", "blue");

// for loops
for(String color : colors){
  ...
}

This increases the syntactic noise in Java programs making them harder to comprehend. Xtend can automatically infer the types of a variable from the type of the right hand side of an assignment. You need to type less and your programs become more readable:

// variable declarations
val greeting = "Hello"
val persons = <String, Person>newHashMap()
val colors = newArrayList("red", "green", "blue")

// for loops
for(color : colors){
  ...
}

If you want to, you can also let Xtend infer the type of fields and the return types of methods:

class Example {
  val greeting = init()
  def init(){
    return "Hello"
  } 
}

Type inference is like having a heated steering wheel in your car, once you get used to it, you cannot imagine using a car in winter without one.

3. Extension Methods

The idea behind extension methods is quite simple: you can add new methods to an object without changing it. For example, given a method:

def removeVowels (String s){
  s.replaceAll("[aeiouAEIOU]", "")
}

In Xtend we can call this method either like in Java:

removeVowels("Hello")

or as an extension method of String:

"Hello".removeVowels

Now, which version is more readable? Extension methods use a simple syntactic trick: the first parameter of a method can either be passed in after opening the parentheses or before the method call. You can either define extension methods in the same class or import static methods as extensions.

import static extension java.util.Collections.*
...
val colors = newArrayList("red", "green", "blue")
colors.sort
colors.printLn // => ["blue", "green", "red"]

However, the real beauty of extension methods reveals when using dependency injection together with fields. For example, given a class Persister, which is responsible for, well, persisting stuff:

class Persister{
  def persist(String s){
    //...
  }
}

In Java, whenever you use the persister in your class, you need to write something like:

persister.persist("My String");

In Xtend, you can declare the persister as an extension field and call persist directly on strings, which makes your code a lot more readable:

class Document {
  
  extension Persister persister
  String content
  
  new(Persister persister) {
    this.persister = persister
  }
  
  def save(){
    content.persist()
  }
}

The advantage is, you can change the implementation of Persister at any time, without sacrificing the readability of your code. By the way, with extension methods the expression problem can be solved with Xtend in an elegant way.

4. Lambda Expressions

What the heck are lambda expressions and why are they useful? Let's start with an example in Java. Given we want to implement a method that converts each string in a list to its uppercase version. A possible solution would be:

public List<String> toUpperCase(List<String> strings) {
  List<String> result = new ArrayList<String>();
  for (String string : strings) {
    result.add(string.toUpperCase());
  }
  return result;
}

But what if we need another function that replaces each string with its lowercase version? Of course, we could create a second method:

public List<String> toLowerCase(List<String> strings) {
  List<String> result = new ArrayList<String>();
  for (String string : strings) {
    result.add(string.toLowerCase());
  }
  return result;
}

The problem with our solution is that we have a redundancy. Both methods create a list and add values to it. A better approach would be to separate the list modification and the string manipulations. We could do this by declaring a new interface:

public interface Function{
  String apply(String input);
}

And use this new interface to encapsulate the list creation and modification into a separate method, which applies the passed in function to each string in the input list and adds the result to a new list:

public List<String> map(List<String> strings, Function toApply) {
  List<String> result = new ArrayList<String>();
  for (String string : strings) {
    result.add(toApply.apply(string));
  }
  return result;
}

We can now refactor our original functions to use the new map function:

public List<String> toLowerCase(List<String> strings) {
  return map(strings, new Function() {
    public String apply(String input) {
      return input.toLowerCase();
    }
  });
}

and

public List<String> toUpperCase(List<String> strings) {
  return map(strings, new Function() {
    public String apply(String input) {
      return input.toUpperCase();
    } 
  });
}

We encapsulated our string manipulation operation into an anonymous class and pass an instance of this class to the map method which applies it to lists. The result is nice and clean code. Maybe a little bit verbose, but we got rid of all redundancies. What we actually did is, we implemented the concept of an anonymous function in Java using interfaces and anonymous classes. Another name for anonymous function is lambda expression. As we have seen, lambda expressions can be really useful for creating modular and reusable code. This is the reason why Xtend provides native support for lambda expressions. A lambda expression can be defined in Xtend within square brackets:

var lambda = [String s | s.length]

The parameter type can be omitted if the type can be coerced from the expression on the left-hand side:

var (String)=>int lambda = [s | s.length]

(String)=>int defines a function type with a string parameter and an int return value. Single parameter functions do not need a parameter declaration and the parameter can be accessed via the implicit variable 'it':

var (String)=>int lambda = [it.length]

...and we can even completely remove the 'it' variable:

var (String)=>int lambda = [length] 
lambda.apply("hello") => 5

Xtend provides a large library of extension functions such as map providing commonly needed functions. We can write our two functions as:

def toUpperCase(List<String> strings){
  strings.map[toUpperCase]
}

and:

def toLowerCase(List<String> strings){
  strings.map[toLowerCase]
}

Which is a lot more concise than the Java version. And again, there is no magic going on here, the generated code is plain and understandable Java code. The generated code is actually similar to the Java example described before.

5. Template Expressions

Xtend has its roots in Xpand, a widely used code generation framework. This is also where Xtend powerful template expressions come from:

val colors = newArrayList("green", "blue", "yellow")
val html = '''
  <html>
    <body>
      My Colors:
      <ul>
          «FOR color : colors»
          <li>«color»</li>
          «ENDFOR»
      </ul>
    </body>
  </html>
'''
println(html)
Template expressions are multiline strings enclosed by '''. These strings can be further enriched with expressions in so called guillemets «expression». Such expressions can be either used to insert the expression result into the resulting string or to control the string generation with loops or conditions.

So far, these features are common to most templating languages. However, where Xtend really shines is when it comes to handling white space. Usually it is pretty hard to generate readable code with proper indentation. Xtend processes whitespaces in a clever way making it easy to generate properly formatted code. You can see in the Eclipse editor clearly which whitespace is generated and which not by the syntax coloring:

Conclusion

This has been my five favorite features of Xtend. For me it is hard to imagine going back normal to Java after using Xtend. The Eclipse tooling is already pretty good with support for refactoring and debugging. There also exists a great testing framework which is based on Xtend. I can definitely recommend giving Xtend a try, especially if you require seamless interoperablity with existing Java code.