Expressions

There are no statements in Xtend. Instead, everything is an expression and has a return value. That allows to use every Xtend expression on the right hand side of an assignment. For example, as a try catch is an expression the following code is legal in Xtend:

val data = try {
  fileContentsToString('data.txt')
catch (IOException e) {
  'dummy data'
}

If fileContentsToString() throws an IOException, it is caught and a default value is returned and assigned to the variable data. In Xtend, expressions appear as initializers of fields or as the bodies of constructors or methods. A method body in Xtend can either be a single block expression or a template expression.

Literals

A literal denotes a fixed unchangeable value. Literals for strings, integers, boolean values, null and Java types are supported.

String Literals

A string literal is a valid expression and returns an instance of java.lang.String of the given value. String literals are enclosed by a pair of single quotes or double quotes allowing to use the respective other unquoted inside the string. Special characters can be quoted with a backslash or defined using Java's unicode notation. On the contrary to Java, Xtend's strings can span multiple lines without the need to quote newline characters.

'Hello World !'
"Hello World !"
'Hello "World" !'
"Hello \"World\" !"
'\u00a1Hola el mundo!'
"Hello 

  World !"

Integer Literals

An integer literal creates an int. There is no signed integer. If you put a minus operator in front of an integer literal it is taken as a UnaryOperator with one argument (the positive integer literal).

42
234254
-1 // an expression consisting of the unary - operator and an integer literal  

Boolean Literals

There are two boolean literals, true and false which correspond to their Java counterpart of type boolean.

Null Literal

The null pointer literal is null. It is a member of any reference type and the only member of the type java.lang.Void.

Type Literals

Type literals are specified using the keyword typeof :

typeof(java.lang.String) // yields java.lang.String.class

Function Types

Xbase introduces closures, and therefore an additional function type signature. On the Java level a closure (or more generally any function object) is just an instance of one of the types in Functions (src), depending on the number of arguments. However, as closures are a very important language feature, a special syntax for function types has been introduced. So instead of writing

Function1<String,Boolean>

one can write

(String)=>boolean

Type Casts

Type cast behave like casts in Java, but have a slightly better readable syntax. Type casts bind stronger than any other operator but weaker than feature calls.

The conformance rules for casts are defined in the Java Language Specification. Here are some examples:

something as MyClass
"" as Object

Infix Operators and Operator Overloading

There are a couple of common predefined infix operators. In contrast to Java, the operators are not limited to operations on certain types. Instead an operator-to-method mapping allows users to redefine the operators for any type just by implementing the corresponding method signature. As an example, the Xtend runtime library contains a class BigDecimalExtensions that defines operators for BigDecimals which allows the following code:

val x=new BigDecimal('2.71')
val y=new BigDecimal('3.14')
x+y    // calls BigDecimalExtension.operator_plus(x,y)

The following defines the operators and the corresponding Java method signatures / expressions.

e1 += e2 e1.operator_add(e2)
e1 || e2 e1.operator_or(e2)
e1 && e2 e1.operator_and(e2)
e1 == e2 e1.operator_equals(e2)
e1 != e2 e1.operator_notEquals(e2)
e1 < e2 e1.operator_lessThan(e2)
e1 > e2 e1.operator_greaterThan(e2)
e1 <= e2 e1.operator_lessEqualsThan(e2)
e1 >= e2 e1.operator_greaterEqualsThan(e2)
e1 -> e2 e1.operator_mappedTo(e2)
e1 .. e2 e1.operator_upTo(e2)
e1 + e2 e1.operator_plus(e2)
e1 - e2 e1.operator_minus(e2)
e1 * e2 e1.operator_multiply(e2)
e1 / e2 e1.operator_divide(e2)
e1 % e2 e1.operator_modulo(e2)
e1 ** e2 e1.operator_power(e2)
! e1 e1.operator_not()
- e1 e1.operator_minus()
The table above also defines the operator precedence in ascending order. The blank lines separate precedence levels. The assignment operator += is right-to-left associative in the same way as the plain assignment operator = is. That is a = b = c is executed as a = (b = c), all other operators are left-to-right associative. Parenthesis can be used to adjust the default precedence and associativity.

Short-Circuit Boolean Operators

If the operators || and && are used in a context where the left hand operand is of type boolean, the operation is evaluated in short circuit mode, which means that the right hand operand might not be evaluated at all in the following cases:

  1. in the case of || the operand on the right hand side is not evaluated if the left operand evaluates to true.
  2. in the case of && the operand on the right hand side is not evaluated if the left operand evaluates to false.

Examples

my.property = 23
myList += 23
x > 23 && y < 23
x && y || z
1 + 3 * 5 * (- 23)
!(x)

Assignments

Local variables can be reassigned using the = operator. Also properties can be set using this operator given the following expression:

myObj.myProperty = "foo"

The compiler first looks up whether there is an accessible Java Field called myProperty on the type of myObj. If there exists such a field it translates to the following Java expression:

myObj.myProperty = "foo";

Remember in Xtend everything is an expression and has to return something. In the case of simple assignments the return value is the value returned from the corresponding Java expression, which is the assigned value.

If there is no accessible field on the left operand's type, a method called setMyProperty (JavaBeans setter method) is looked up. It has to take one argument of the type (or a super type) of the right hand operand. The return value will be whatever the setter method returns (which usually is void). As a result the compiler translates to:

myObj.setMyProperty("foo")

Variable Declarations

Variable declarations are only allowed within blocks. They are visible in any subsequent expressions in the block.

A variable declaration starting with the keyword val denotes a so called value, which is essentially a final (i.e. unsettable) variable. In some cases, one needs to update the value of a reference. In such situations the variable needs to be declared with the keyword var, which stands for 'variable'.

A typical example for using var is a counter in a loop:

{
  val max = 100
  var i = 0
  while (i < max) {
    println("Hi there!")
    i = i + 1
  }
}

Although overriding or shadowing variables from outer scopes is allowed, it is usually only used to overload the implicit variable it, in order to subsequently access an object's features in an unqualified manner.

Variables declared outside a closure using the var keyword are not accessible from within a closure.

Typing

The return type of a variable declaration expression is always void. The type of the variable itself can either be explicitly declared or be inferred from the right hand side expression. Here is an example for an explicitly declared type:

var List<String> msg = new ArrayList(); 

In such cases, the type of the right hand expression must conform to the type of the expression on the left side.

Alternatively the type can be left out and will be inferred from the initialization expression:

var msg = new ArrayList<String>(); // -> type ArrayList<String>  

Feature Calls

A feature call is used to invoke members of objects, such as fields and methods, but also can refer to local variables and parameters, which are made available for the current expression's scope.

Property Access

Feature calls are directly translated to their Java equivalent with the exception, that for calls to properties an equivalent rule as described in section propertyAssignment applies. That is, for the following expression

myObj.myProperty

the compiler first looks for an accessible field in the type of myObj. If no such field exists it looks for a method called myProperty() before it looks for the getter methods getMyProperty(). If none of these members can be found the expression is unbound and a compiliation error is indicated.

Implicit Variables this and it

If the current scope contains a variable named this or it, the compiler will make all its members available to the scope. That is one of

it.myProperty
this.myProperty

is a valid expression

myProperty

is valid as well and is equivalent, as long as there is no local variable 'myProperty' on the scope, which would have higher precedence.

As this is bound to the surrounding object in Java, it can be used in finer-grained constructs such as function parameters. That is why it.myProperty has higher precedence than this.myProperty. it is also the default parameter name in closures.

Null-Safe Feature Call

Checking for null references can make code very unreadable. In many situations it is ok for an expression to return null if a receiver was null. Xtend supports the safe navigation operator ?. to make such code better readable.

Instead of writing

if (myRef != null) myRef.doStuff()

one can write

myRef?.doStuff()

Constructor Call

Construction of objects is done by invoking Java constructors. The syntax is exactly as in Java, e.g.

new String()
new ArrayList<BigDecimal>()

Closures

A closure is a literal that defines an anonymous function. A closure also captures the current scope, so that any final variables and parameters visible at construction time can be referred to in the closure's expression.

val func = [String s | s.length>3]

The surrounding square brackets are optional if the closure is the single argument of a method invocation. That is you can write

myList.findFirst(e | e.name==null)

instead of

myList.findFirst([e | e.name==null])

But in all other cases the square brackets are mandatory.

Typing

Closures are expressions which produce Function objects. The type is a function type, consisting of the types of the parameters as well as the return type. The return type is never specified explicitly but is always inferred from the expression. The parameter types can be inferred if the closure is used in a context where this is possible.

For instance, given the following Java method signature:

public <T> T find(List<T> list, Function1<T,Boolean> predicate) 

the type of the parameter can be inferred. Which allows users to write:

newArrayList("Foo""Bar").find(e | e=="Bar")

instead of

newArrayList("Foo""Bar").find(String e | e=="Bar")

Here are some more examples:

[| "foo"]   // closure without parameters
[String s | s.toUpperCase()] // explicit argument type
[a,b,c | a+b+c] // inferred argument types

Function Mapping

An Xtend closure is a Java object of one of the Function interfaces shipped with the runtime library of Xtend. There is an interface for each number of parameters (current maximum is six parameters). The names of the interfaces are

or if the return type is void.

In order to allow seamless integration with existing Java libraries such as the JDK or Google Guava (formerly known as Google Collect) closures are automatically coerced to expected types if those types declare only one method (methods from java.lang.Object do not count).

As a result given the method IterableExtensions.sort(Iterable<T>, Comparator<? super T>) is available as an extension method, it can be invoked like this

newArrayList( 'aaa''bb''c' ).sort(
  e1, e2 | if ( e1.length > e2.length ) {
        -1 
       } else if ( e1.length < e2.length ) { 
        1
       } else { 
        0
       })

Implicit Parameter it

If a closure has a single parameter whose type can be inferred, the declaration of the parameter can be ommitted. Use it to refer to the parameter inside the closure's body.

val (String)=>String function = [toUpperCase]  // equivalent to [it | it.toUpperCase]

Exceptions in Closures

Checked exceptions that are thrown in the body of a closure are rethrown using the sneaky-throw technique, i.e. you do not have to declare them explicitly.

Builder Syntax

If the last argument of a function call is a closure, it can be appended after the parenthesized parameter list. In combination with the implicit it parameter, skipping empty parentheses, and extension methods, this yields a very concise syntax.

val fruit = newArrayList('apple''pear''lemon')
fruit.map[toUpperCase]  // same as fruit.map([it | it.toUpperCase])

This feature is especially useful when you are building object trees. A common pattern is to provide a set of extension functions taking two parameters: the parent object and a closure to initialize the new child. Here is an example for creating a simple tree of Nodes:

class Node {
  String label
  List<Node> children
  
  def createNode(Node parent, (Node)=>void initializer) {
    val child=new Node()
    initializer.apply(child)
    child
  }

  def tree() {
    createNode(null) [
      label="root"
      children += createNode [
        label="child0"
      ]
      children += createNode [
        label="child1"
      ]
    ]
  }
}

If Expression

An if expression is used to choose two different values based on a predicate. While it has the syntax of Java's if statement it behaves like Java's ternary operator (predicate ? thenPart : elsePart), i.e. it is an expression that returns a value. Consequently, you can use if expressions deeply nested within expressions.

An expression

if (p) e1 else e2

results in either the value e1 or e2 depending on whether the predicate p evaluates to true or false. The else part is optional which is a shorthand for else null. That means

if (foo) x 

is the a short hand for

if (foo) x else null

The type of an if expression is the common super type of the return types T1 and T2 of the two expression e1 and e2.

Switch Expression

The switch expression is different from Java's. First, there is no fall through which means only one case is evaluated at most. Second, the use of switch is not limited to certain values but can be used for any object reference instead. For a switch expression

switch e {
  case e1 : er1
  case e2 : er2
  ...
  case en : ern
  default : er
}

the main expression e is evaluated first and then each case sequentially. If the switch expression contains a variable declaration using the syntax known from section forLoop, the value is bound to the given name. Expressions of type java.lang.Boolean or boolean are not allowed in a switch expression.

The guard of each case clause is evaluated until the switch value equals the result of the case's guard expression or if the case's guard expression evaluates to true. Then the right hand expression of the case evaluated and the result is returned.

If none of the guards matches the default expression is evaluated an returned. If no default expression is specified the expression evaluates to null.

Example:

switch myString {
  case myString.length>5 : "a long string."
  case 'some' : "It's some string."
  default : "It's another short string."
}

Type guards

In addition to the case guards one can add a so called Type Guard which is syntactically just a type reference preceding an optional case keyword. The compiler will use that type for the switch expression in subsequent expressions. Example:

var Object x = ...;
switch x {
  String case x.length()>0 : x.length()
  List<?> : x.size()
  default : -1
}

Only if the switch value passes a type guard, i.e. an instanceof operation returns true, the case's guard expression is executed using the same semantics explained previously. If the switch expression contains an explicit declaration of a local variable or the expression references a local variable, the type guard acts like a cast, that is all references to the switch value will be of the type specified in the type guard.

Blocks

The block expression allows to have imperative code sequences. It consists of a sequence of expressions, and returns the value of the last expression. The return type of a block is also the type of the last expression. Empty blocks return null. Variable declarations are only allowed within blocks and cannot be used as a block's last expression.

A block expression is surrounded by curly braces and contains at least one expression. It can optionally be terminated by a semicolon.

Here are two examples:

{
  doSideEffect("foo")
  result
}

{
  var x = greeting();
  if (x.equals("Hello ")) {
    x+"World!"
  } else {
    x;
  }
}

For Loop

The for loop

for (T1 variable : arrayOrIterable) expression

is used to execute a certain expression for each element of an array of an instance of java.lang.Iterable. The local variable is final, hence canot be updated.

The return type of a for loop is void. The type of the local variable can be left out. In that case it is inferred from the type of the array or java.lang.Iterable returned by the iterable expression.

for (String s : myStrings) {
  doSideEffect(s)
}

for (s : myStrings)
  doSideEffect(s)

While Loop

A while loop

while (predicate) expression

is used to execute a certain expression unless the predicate is evaluated to false. The return type of a while loop is void.

while (true) {
  doSideEffect("foo")
}

while ((i=i+1) < max) 
  doSideEffect("foo")

Do-While Loop

A do-while loop

do expression while (predicate)

is used to execute a certain expression unless the predicate is evaluated to false. The difference to the while loop is that the execution starts by executing the block once before evaluating the predicate for the first time. The return type of a do-while loop is void.

do {
  doSideEffect("foo");
while (true)


do doSideEffect("foo"while ((i=i+1)<max)

Return Expression

Although an explicit return is often not necessary, it is supported. In a closure for instance a return expression is always implied if the expression itself is not of type void. Anyway you can make it explicit:

listOfStrings.map(e| {
  if (e==null
    return "NULL"
  e.toUpperCase
}) 

Throwing Exceptions

Like in Java it is possible to throw java.lang.Throwable. The syntax is exactly the same as in Java.

{
  ...
  if (myList.isEmpty)
    throw new IllegalArgumentException("the list must not be empty")
  ...
}   

Try, Catch, Finally

The try-catch-finally expression is used to handle exceptional situations. You are not forced to declare checked exceptions, if you do not catch checked exceptions they are rethrown in a wrapping runtime exception. Other than that the syntax again is like the one known from Java.

try {
  throw new RuntimeException()
catch (NullPointerException e) {
  // handle e
finally {
  // do stuff
}

Template Expressions

Templates allow for readable string concatenation, which is the main thing you do when writing a code generator. Template are surrounded by triple single or double quotes. The text of a template can be interrupted by expressions in french quotes «». One way to type these guillemets is to use content assist inside the template.

Let us have a look at an example of how a typical method with template expressions looks like:

def toClass(Entity e) '''
  package «e.packageName»;

  «placeImports»

  public class «e.name» «IF e.superClass!=null»extends «e.superClass»«ENDIF» {
    «FOR e.members»
      «member.toMember»
    «ENDFOR»
  }
'
''

A template is actually an expression, which means it can occur everywhere where an expression is expected. For instance in conjunction the powerful switch expression:

toMember(Member m) {
  switch m {
    Field : '''private «m.type» «m.name» ;'''
    Method case isAbstract : ''' abstract «...'''
    Method : ''' ..... '''
  }
}

Conditions in Templates

There is a special IF to be used within templates which is identical in syntax and meaning to the old IF from Xpand. Note that you could also use the if expression, but since it has not an explicit terminal token, it is not as readable in that context.

Loops in Templates

Also the FOR statement is available and can only be used in the context of a template. It also supports the SEPARATOR from Xpand. In addition, a BEFORE expression can be defined that is only evaluated if the loop is at least evaluated once before the very first iteration. Consequently AFTER is evaluated after the last iteration if there is any element.

Typing

The rich string is translated to an efficient string concatenation and the return type of a rich string is CharSequence which allows room for efficient implementation.

White Space Handling

One of the key features of templates is the smart handling of white space in the template output. The white space is not written into the output data structure as is but preprocessed. This allows for readable templates as well as nicely formatted output. This can be achieved by applying three simple rules when the rich string is evaluated.

  1. An evaluated rich string as part of another string will be prefixed with the current indentation of the caller before it is inserted into the result.
  2. Indentation in the template that is relative to a control structure will not be propagated to the output string. A control structure is a FOR-loop or a condition (IF) as well as the opening and closing marks of the rich string itself. The indentation is considered to be relative to such a control structure if the previous line ends with a control structure followed by optional white space. The amount of white space is not taken into account but the delta to the other lines.
  3. Lines that do not contain any static text which is not white space but do contain control structures or invocations of other templates which evaluate to an empty string, will not appear in the output.

The behavior is best described with a set of examples. The following table assumes a data structure of nested nodes.

class Template {
  print(Node n) '''
    node «n.name» {}
  '
''
}

node NodeName {}

The indentation before node «n.name» will be skipped as it is relative to the opening mark of the rich string and thereby not considered to be relevant for the output but only for readability of the template itself.

class Template {
  print(Node n) '''
    node «n.name» {
      «IF hasChildren»
        «n.children.map[print]»
      «ENDIF»
    }
  '
''
}

node Parent{
  node FirstChild {
  }
  node SecondChild {
    node Leaf {
    }
  }
}

As in the previous example, there is no indentation on the root level for the same reason. The first nesting level has only one indentation level in the output. This is derived from the indentation of the IF hasChildren condition in the template which is nested in the node. The additional nesting of the recursive invocation children.map[print] is not visible in the output as it is relative the the surrounding control structure. The line with IF and ENDIF contain only control structures thus they are skipped in the output. Note the additional indentation of the node Leaf which happens due to the first rule: Indentation is propagated to called templates.