At a first glance an Xtend file pretty much looks like a Java file. It starts with a package declaration followed by an import section and a class definition. The class in fact is directly translated to a Java class in the corresponding Java package. A class can have constructors, fields and methods.
Here is an example:
package com.acme
import java.util.List
class MyClass {
String name
new(String name) {
this.name = name
}
def String first(List<String> elements) {
elements.get(0)
}
}
Package declarations look like in Java. There are two small differences:
package com.acme
The ordinary imports of type names are equivalent to the imports known from Java. Again one can escape any names conflicting with keywords using a ^. In contrast to Java, the terminating semicolon is optional. Xtend also features static imports but allows only a wildcard * at the end, i.e. you currently cannot import single members using a static import. Non-static wildcard imports are deprecated for the benefit of better tooling.
As in Java all classes from the java.lang package are implicitly imported.
import java.math.BigDecimal
import static java.util.Collections.*
Static methods of helper classes can also be imported as extensions. See the section on extension methods for details.
The class declaration reuses a lot of Java's syntax but still is a bit different in some aspects: Java's default "package private" visibility does not exist in Xtend. As an Xtend class is compiled to a top-level Java class and Java does not allow private or protected top-level classes any Xtend class is public. It is possible to write public explicitly.
Since version 2.3, multiple class declaration per file are supported. Each of these classes is compiled to a separate top-level Java class.
Abstract classes are defined using the abstract modifier as in Java. See also section abstractMethods on abstract methods.
Xtend's approach to inheritance is conceptionally the same as in Java. Single inheritance of classes as well as implementing multiple interfaces is supported. Xtend classes can of course extend other Xtend classes, and even Java classes can inherit from Xtend classes.
The most simple class looks like this:
class MyClass {
}
A more advanced generic class declaration in Xtend:
class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess,
Cloneable, java.io.Serializable {
...
}
An Xtend class can define one or more constructors. Unlike Java you do not have to repeat the name of the class over and over again, but use keyword new to declare a constructor. Constructors can also delegate to other constructors using this(args...) in their first line.
class MyClass extends AnotherClass {
new(String s) {
super(s)
}
new() {
this("default")
}
}
The same rules with regard to inheritance apply as in Java, i.e. if the super class does not define a no-argument constructor, you have to explicitly call one using super(args...) as the first expression in the body of the constructor.
The default visibility of constructors is public but you can also specify protected or private.
A field can have an initializer. Final fields are declared using val, while var introduces a non-final field and can be ommitted. Yet, if an initializer expression is present, the type of a field can be skipped after val and var. Fields marked as static will be compiled to static Java fields.
class MyClass {
int count = 1
static boolean debug = false
var name = 'Foo' // type String is inferred
val UNIVERSAL_ANSWER = 42 // final field with inferred type int
...
}
The default visibility is private. You can also declare it explicitly as being public, protected, or private.
A specialty of Xtend are fields that provide extension methods which are covered in their own section.
Xtend methods are declared within a class and are translated to a corresponding Java method with exactly the same signature. The only exceptions are dispatch methods, which are explained later.
def String first(List<String> elements) {
elements.get(0)
}
The default visibility of a plain method is public. You can explicitly declare it as being public, protected, or private.
Xtend supports the static modifier for methods:
def static createInstance() {
new MyClass('foo')
}
As in Java 5, Xtend allows vararg parameters:
def printAll(String... strings) {
strings.forEach[ s | println(s) ]
}
An abstract method in Xtend just does not define a body and must be declared within an abstract class. Also specifying the return type is mandatory since it cannot be inferred.
abstract class MyAbstractClass() {
def String abstractMethod() // no body
}
Methods can override other methods from the super class or implement interface methods using the keyword override. If a method overrides a method from a super type, the override keyword is mandatory and replaces the keyword def. As in Java final methods cannot be overridden by subclasses.
Example:
override String second(List<String> elements) {
elements.get(1)
}
Xtend does not force you to catch or redeclare checked exceptions. Nevertheless, you can still declare the exceptions thrown in a method's body using the same throws clause as in Java.
If you do not declare checked exceptions in your method but they are possibly thrown in your code, the compiler will rethrow the checked exception silently (using the sneaky-throw technique introduced by Lombok).
/*
* throws an Exception
*/
def void throwException() throws Exception {
throw new Exception
}
/*
* throws an Exception without declaring it
*/
def void sneakyThrowException() {
throw new Exception
}
If the return type of a method can be inferred from its body it does not have to be declared.
That is the method
def String second(List<String> elements) {
elements.get(1)
}
could be declared like this:
def second(List<String> elements) {
elements.get(1)
}
This does not work for abstract method declarations as well as if the return type of a method depends on a recursive call of the same method.
You can specify type parameters just like in Java. To generalize the method from the previous section, you would declare it like this:
def <T> second(List<T> elements) {
elements.get(1)
}
Also bounds and the like are supported and share the same syntax as defined in the the Java Language Specification
Generally, method binding works just like method binding in Java. Method calls are bound based on the static types of arguments. Sometimes this is not what you want. Especially in the context of extension methods you would like to have polymorphic behavior.
A dispatch method is marked using the keyword dispatch.
def dispatch printType(Number x) {
"it's a number"
}
def dispatch printType(Integer x) {
"it's an int"
}
For a set of visible dispatch methods in the current type hierarchy sharing the same name and the same number of arguments, the compiler infers a synthetic method (the dispatcher) using the common super type of all declared arguments. The actual dispatch methods are reduced in visibility and renamed (prepending an underscore) so that client code always binds to the dispatcher method.
For the two dispatch methods in the example above the following Java code would be generated:
protected String _printType(final Number x) {
return "it\'s a number";
}
protected String _printType(final Integer x) {
return "it\'s an int";
}
public String printType(final Number x) {
if (x instanceof Integer) {
return _printType((Integer)x);
} else if (x != null) {
return _printType(x);
} else {
throw new IllegalArgumentException("Unhandled parameter types: " +
Arrays.<Object>asList(x).toString());
}
}
Note that the instanceof cascade is ordered such that more specific types come first.
The default visibility of the underscore methods is protected. If all dispatch methods explicitly declare the same visibility, this will be the visibility of the inferred dispatcher, too. Otherwise it is public. The comparison of the type parameters goes from left to right. That is in the following example, the second method declaration is considered more specific since its first parameter type is the most specific:
def dispatch printTypes(Number x, Integer y) {
"it's some number and an int"
}
def dispatch printTypes(Integer x, Number y) {
"it's an int and a number"
}
generates the following Java code :
public String printTypes(final Number x, final Number y) {
if (x instanceof Integer
&& y != null) {
return _printTypes((Integer)x, y);
} else if (x != null
&& y instanceof Integer) {
return _printTypes(x, (Integer)y);
} else {
throw new IllegalArgumentException("Unhandled parameter types: " +
Arrays.<Object>asList(x, y).toString());
}
}
As you can see a null reference is never a match. If you want to fetch null you can declare a dispatch case using the type Void.
def dispatch printType(Number x) {
"it's some number"
}
def dispatch printType(Integer x) {
"it's an int"
}
def dispatch printType(Void x) {
"it's null"
}
This compiles to the following Java code:
public String printType(final Number x) {
if (x instanceof Integer) {
return _printType((Integer)x);
} else if (x != null) {
return _printType(x);
} else if (x == null) {
return _printType((Void)null);
} else {
throw new IllegalArgumentException("Unhandled parameter types: " +
Arrays.<Object>asList(x).toString());
}
}
Any visible Java methods from super types conforming to the compiled form of a dispatch method are also included in the dispatch. Conforming means they have the right number of arguments and have the same name (starting with an underscore).
For example, consider the following Java class :
public abstract class AbstractLabelProvider {
protected String _label(Object o) {
// some generic implementation
}
}
and the following Xtend class which extends the Java class :
class MyLabelProvider extends AbstractLabelProvider {
def dispatch label(Entity it) {
name
}
def dispatch label(Method it) {
name+"("+params.join(",")+"):"+type
}
def dispatch label(Field it) {
name+type
}
}
The resulting dispatch method in the generated Java class MyLabelProvider would then look like this:
public String label(final Object it) {
if (it instanceof Entity) {
return _label((Entity)it);
} else if (it instanceof Field) {
return _label((Field)it);
} else if (it instanceof Method) {
return _label((Method)it);
} else if (it != null) {
return super._label(it);
} else {
throw new IllegalArgumentException("Unhandled parameter types: " +
Arrays.<Object>asList(it).toString());
}
}
Also static dispatch methods are supported. But you cannot mix static and non-static dispatch methods.
The syntax and semantics for annotations is exactly like defined in the Java Language Specification. Annotations are available on classes, fields, methods and parameters. Here is an example:
@TypeAnnotation("some value")
class MyClass {
@FieldAnnotation(children = {@MyAnno(true), @MyAnno(false)})
String myField
@MethodAnnotation(children = {@MyAnno(true), @MyAnno})
def String myMethod(@ParameterAnnotation String param) {
//...
}
}
Certain annotations defined in the library have a special effect on how the code is translated to Java. These annotations are explained in section.
Extension methods allow to add new methods to existing types without modifying them. This feature is actually where Xtend got its name from. They are based on a simple syntactic trick: Instead of passing the first argument of an extension method inside the parentheses of a call, the method is called on the argument parameter as if it was one of its members.
"hello".toFirstUpper() // calls toFirstUper("hello")
Method calls in extension syntax often result in more readable code, as they are chained rather than nested. Another benefit of extensions is that you can add methods which are specific to a certain context or layer of your application. You might for instance not want to put UI-specific methods and dependencies to your domain model classes. Therefore such functionality is often defined in static methods or methods in some "service class". That works, but the code is less readable and less object-oriented if you call methods like this. In Java for instance you often see code like this:
persistenceManager.save(myObject);
Without tying your entities to the persistenceManager, extension methods allow you to write
myObject.save
There are different ways to make methods available as extensions, which are described in the following.
The library puts a lot of very useful extension methods on existing types from the Java SDK without any further ado.
"hello".toFirstUpper // calls StringExtensions.toFirstUpper(String)
listOfStrings.map[ toUpperCase ] // calls ListExtensions.<T, R>map(List<T> list, Function<? super T, ? extends R> mapFunction)
Have a look at the JavaDoc to see what is there:
All visible non-static methods of the current class and its super types are automatically available as extensions. For example
class MyClass {
def doSomething(Object obj) {
// do something with obj
}
def extensionCall(Object obj) {
obj.doSomething() // calls this.doSomething(obj)
}
}
Local static methods have to be made available through an import like any other static method.
In Java, you would usually write a helper class with static methods to decorate an existing class with additional behavior. In order to integrate such static helper classes, Xtend allows to put the keyword extension after the static keyword of a static import thus making all imported static functions available as extensions methods.
The following import declaration
import static extension java.util.Collections.*
allows to use its methods like this:
new MyClass().singletonList()
// calls Collections.singletonList(new MyClass())
By adding the extension keyword to a field declaration, its instance methods become extension methods.
Imagine you want to have some layer specific functionality on a class Person. Let us say you are in a servlet-like class and want to persist a Person using some persistence mechanism. Let us assume Person implements a common interface Entity. You could have the following interface
interface EntityPersistence {
public save(Entity e);
public update(Entity e);
public delete(Entity e);
}
And if you have obtained an instance of that type (through a factory or dependency injection or what ever) like this:
class MyServlet {
extension EntityPersistence ep = Factory.get(typeof(EntityPersistence))
...
}
You are able to save, update and delete any entity like this:
val Person person = ...
person.save // calls ep.save(person)
person.name = 'Horst'
person.update // calls ep.update(person)
person.delete // calls ep.delete(person)
Using the extension modifier on fields has a significant advantage over static extension imports: Your code is not bound to the actual implementation of the extension method. You can simply exchange the component that provides the referenced extension with another implementation from outside, by providing a different instance. No matter whether you do so via a factory, a dependency injection container or simply using a setter.