Xtend does not have statements. Instead, everything is an expression and has a return value. That allows to compose your code in interesting ways. For example, you can have a try catch expression on the right hand side of an assignment:
val data = try {
fileContentsToString('data.txt')
} catch (IOException e) {
'dummy data'
}
If fileContentsToString() throws an IOException, it is caught and the string 'dummy data' is assigned to the value data. Expressions can appear as initializers of fields, the body of constructors or methods and as values in annotations. A method body can either be a block expression or a template expression.
A literal denotes a fixed unchangeable value. Literals for strings, numbers, booleans, null and Java types are supported.
A string literal is of type String (just like in Java). String literals are enclosed in a pair of single quotes or double quotes. We mostly use single quotes because the signal-to-noise ration is a bit better, but generally you should use the terminals which are least likely occure in the actual string. Special characters can be quoted with a backslash or defined using Java's unicode notation. Contrary to Java, strings can span multiple lines.
'Hello World !'
"Hello World !"
'Hello "World" !'
"Hello \"World\" !"
"Hello
World !"
Xtend supports roughly the same number literals as Java with a few differences. First, there are no signed number literals. If you put a minus operator in front of an number literal it is taken as a unary operator with one argument (the positive number literal). Second, as in Java 7, you can separate digits using _ for better readability of large numbers. An integer literal creates an int, a long (suffix L) or a BigInteger (suffix BI). There are no octal numbers
42
1_234_567_890
0xbeef // hexadecimal
077 // decimal 77 (*NOT* octal)
-1 // an expression consisting of the unary - operator and an integer literal
42L
0xbeef#L // hexadecimal, mind the '#'
0xbeef_beef_beef_beef_beef#BI // BigInteger
A floating-point literal creates a double (suffix D or none), a float (suffix F) or a BigDecimal (suffix BD). If you use a . you have to specify both, the integer and the fractional part of the mantissa. There are only decimal floating-point literals.
42d // double
0.42e2 // implicit double
0.42e2f // float
4.2f // float
0.123_456_789_123_456_789_123_456_789e2000bd // BigDecimal
There are two boolean literals, true and false which correspond to their Java counterpart of type boolean.
The null pointer literal null has exactly the same semantics as in Java.
Type literals are specified using the keyword typeof :
typeof(java.lang.String) // yields java.lang.String.class
A type cast behaves exactly like casts in Java, but has a slightly more 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
42 as Integer
Although casts are supported you might want to use a switch with a type guard or a dispatch method as a better and safer alternative.
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 (src) that defines operators for BigDecimals which allows the following code:
val x = 2.71BD
val y = 3.14BD
val sum = x + y // calls BigDecimalExtension.operator_plus(x,y)
This is the complete list of all available operators and their corresponding method signatures:
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_doubleArrow(e2) |
e1 << e2 | e1.operator_doubleLessThan(e2) |
e1 >> e2 | e1.operator_doubleGreaterThan(e2) |
e1 <<< e2 | e1.operator_tripleLessThan(e2) |
e1 >>> e2 | e1.operator_tripleGreaterThan(e2) |
e1 <> e2 | e1.operator_diamond(e2) |
e1 ?: e2 | e1.operator_elvis(e2) |
e1 <=> e2 | e1.operator_spaceship(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() |
If the operators || and && are bound to the library methods BooleanExtensions.operator_and(boolean l, boolean r) (src) resp. BooleanExtensions.operator_or(boolean l, boolean r) (src) the operation is evaluated in short circuit mode. That means that the right hand operand might not be evaluated at all in the following cases:
Still you can overload these operators for your types or even override it for booleans, in which case both operands are always evaluated and the defined method is invoked, i.e. no short-circuit execution is happening.
my.property = 23
myList += 23
x > 23 && y < 23
x && y || z
1 + 3 * 5 * (- 23)
!(x)
Local variables can be reassigned using the = operator.
var greeting = 'Hello'
if (isInformal)
greeting = 'Hi'
Of course, also non-final fields can be set using an assignment:
myObj.myField = 'foo'
The lack of properties in Java leads to a lot of syntactic noise when working with data objects. As Xtend is designed to integrate with existing Java APIs it respects the Java Beans convention, hence you can call a setter using an assignment:
myObj.myProperty = 'foo' // calls myObj.setMyProperty("foo")
The setter is only used if the field is not accessible from the given context. That is why the @Property annotation would rename the local field to _myProperty.
The return type of an assignment is the type of the right hand side, in case it is a simple assignment. If it is translated to a setter method it yields whatever the setter method returns.
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
}
}
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 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
}
}
Shadowing variables from outer scopes is not allowed, the only exception is the implicit variable it.
Variables declared outside a lambda expression using the var keyword are not accessible from within a lambda expressions.
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> // -> msg is of type ArrayList<String>
A simple name can refer to a local field, variable or parameter. In addition it can point to a method with zero arguments, since empty parenthesis are optional.
If there is no field with the given name and also no method with the name and zero parameters accessible, a simple name binds to a corresponding Java-Bean getter method if available :
myObj.myProperty // myObj.getMyProperty() (.. in case myObj.myProperty is not visible.)
Like in Java an instance of the class is bound to this. Which allows for either qualifying field access or method invocations like in:
this.myField
or omit the receiver:
myField
You can use the variable name it to get the same behavior for any variable or parameter:
val it = new Person
name = 'Horst' // translates to 'it.setName("Horst");'
Another speciality of the variable it is that it can be shadowed. This is especially useful when used together with lambda expressions.
As this is bound to the surrounding object in Java, it can be used in finer-grained constructs such as lambda expressions. That is why it.myProperty has higher precedence than this.myProperty.
For accessing a static field or method you have to use the double colon :: like in this example:
MyClass::myField
MyClass::myMethod('foo')
Alternatively you could import the method using a static import.
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 calls have the same syntax as in Java. The only difference is that empty parenthesis are optional:
new String() == new String
new ArrayList<BigDecimal>() == new ArrayList<BigDecimal>
A lambda expression is basically a piece of code, which is wrapped in an object to pass it around. As a Java developer it is best to think of a lambda expression as an anonymous class, i.e. like in the following Java code :
// Java Code!
final JTextField textField = new JTextField();
textField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
textField.setText("Something happened!");
}
});
This kind of anonymous classes can be found everywhere in Java code and have always been the poor-man's replacement for lambda expressions in Java.
Xtend not only supports lambda expressions, but offers an extremely dense syntax for it. That is the code above can be written in Xtend like this:
val textField = new JTextField
textField.addActionListener([ ActionEvent e |
textField.text = "Something happened!"
])
As you might have guessed, a lambda expression is surrounded by square brackets (inspired from Smalltalk). Also a lambda expression like a method declares parameters. The lambda above has one parameter called e which is of type ActionEvent. You do not have to specify the type explicitly because it can be inferred from the context:
textField.addActionListener([ e |
textField.text = "Something happened!"
])
Also as lambdas with one parameter are a common case, there is a special short hand for them, which is to leave the declaration including the vertical bar out. The name of the single variable will be it in that case:
textField.addActionListener([
textField.text = "Something happened!"
])
A lambda expression with zero arguments is written like this (note the bar after the opening bracket):
val Runnable runnable = [ |
println("Hello I'm executed!")
]
When a method call's last parameter is a lambda it can be passed right after the parameter list. For instance if you want to sort some strings by their length, you could write :
Collections::sort(someStrings) [ a, b |
a.length - b.length
]
which is just the same as writing
Collections::sort(someStrings, [ a, b |
a.length - b.length
])
Since you can leave out empty parenthesis for methods which get a lambda as their only argument, you can reduce the code above further:
textField.addActionListener [
textField.text = "Something happened!"
]
A lambda expression 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. That is exactly what we did with the variable textField.
Lambdas are expressions which produce Function objects. The type of a lambda expression generally depends on the target type, as seen in the previous examples. That is, the lambda expression can coerce to any interface which has declared only one method (in addition to the ones inherited from Object). This allows for using lambda expressions in many existing Java APIs directly.
However, if you write a lambda expression without having any target type expectation, like in the following assignment:
val toUpperCaseFunction = [ String s | s.toUpperCase ] // inferred type is (String)=>String
The type will be one of the types found in Functions (src) or Procedures (src). It is a procedure if the return type is void, otherwise it is a function.
Xtend supports a shorthand syntax for function types. Instead of writing Function1<? super String,? extends String> which is what you will find in the generated Java code, you can simply write (String)=>String.
Example:
val (String)=>String stringToStringFunction = [ toUpperCase ]
// or
val Function1<? super String,? extends String> same = [ toUpperCase ]
// or
val stringToStringFunction2 = [ String s | s.toUpperCase ] // inferred type is (String)=>String
Checked exceptions that are thrown in the body of a closure but not declared in the implemented method of the target type are rethrown using the sneaky-throw technique. Of course you can always catch and handle them.
An if expression is used to choose between two different values based on a predicate.
An expression
if (p) e1 else e2
if (foo) x
is 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.
While the if expression has the syntax of Java's if statement it behaves more like Java's ternary operator (predicate ? thenPart : elsePart), because it is an expression and returns a value. Consequently, you can use if expressions deeply nested within expressions:
val name = if (firstName != null) firstName + ' ' + lastName else lastName
The switch expression is very different from Java's switch statement. 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. Object.equals(Object) is used to compare the value in the case with the one you are switching over. Given the following example:
switch myString {
case myString.length>5 : "a long string."
case 'some' : "It's some string."
default : "It's another short string."
}
the main expression numberAsText is evaluated first and then compared to each case sequentially. If the case expression is of type boolean, the case matches if the expression evaluates to true. If it is not of type boolean it is compared to the value from the main expression using Object.equals(Object).
If a case is a match, that is it evaluates to true or the result equals the one we are switching over, the case expression after the colon is evaluated and is the result of the whole expression.
Instead of or in addition to the case guard you can specify a type guard. The case only matches if the switch value conforms to this type. A case with both a type guard and a predicate only matches if both match. If the switch value is a variable, this variable is automatically casted to the given type within the predicate and the case's body.
def length(Object x) {
switch x {
String case x.length > 0 : x.length // length is defined for String
List<?> : x.size // size is defined for List
default : -1
}
}
Switches with type guards are a safe and much more readable alternative to instance of / casting orgies you might know from Java.
The for loop
for (T1 variable : arrayOrIterable) expression
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)
A while loop
while (predicate) expression
while (true) {
doSideEffect("foo")
}
while ((i=i+1) < max)
doSideEffect("foo")
A do-while loop
do expression while (predicate)
do {
doSideEffect("foo");
} while (true)
do doSideEffect("foo") while ((i=i+1)<max)
A method or lambda expression automatically returns the value of its expression. If it is a block expression this is the value of the last expression in it. However, sometimes you want to return early or make it explicit.
The syntax is just like in Java:
listOfStrings.map(e| {
if (e==null)
return "NULL"
e.toUpperCase
})
Throwing Throwables up the call stack has the same semantics and syntax as in Java.
{
...
if (myList.isEmpty)
throw new IllegalArgumentException("the list must not be empty")
...
}
The try-catch-finally expression is used to handle exceptional situations. Checked exceptions are treated like runtime exceptions. You can but do not have to catch them as they will be silently rethrown (see the section on declared exceptions). The syntax again is like Java.
try {
throw new RuntimeException()
} catch (NullPointerException e) {
// handle e
} finally {
// do stuff
}
For try-catch it is again beneficial that it is an expression, because you can write code like the following and do not have to rely on non-final variables:
val name = try {
person.name
} catch (NullPointerException e) {
"no name"
}
Templates allow for readable string concatenation. Templates are surrounded by triple single quotes ('''). A template expression can span multiple lines and expressions can be nested which are evaluated and their toString() representation is automatcially inserted at that position.
The terminals for interpolated expression are so called guillemets «expression». They read nicely and are not often used in text so you seldomly need to escape them. These escaping conflicts are the reason why template languages often use longer character sequences like e.g. <%= expression %> in JSP, for the price of worse readability. The downside with the guillemets in Xtend is that you will have to have a consistent encoding. Always use UTF-8 and you are good.
If you use the Eclipse plug-in - which is recommended - the guillemets will be inserted on content assist within a template. They are additionally bound to CTRL+SHIFT+< and CTRL+SHIFT+ for « and » respectively. On a Mac they are as well bound to alt+q («) and alt+Q (»).
Let us have a look at an example of how a typical method with a template expressions looks like:
def someHTML(String content) '''
<html>
<body>
«content»
</body>
</html>
'''
As you can see, template expressions can be used as the direct body of a method. If an interpolation expression evaluates to null an empty string is added.
Template expressions can occur everywhere. Here is an example showing it in conjunction with the powerful switch expression:
def toText(Node n) {
switch n {
Contents : n.text
A : '''<a href="«n.href»">«n.applyContents»</a>'''
default : '''
<«n.tagName»>
«n.applyContents»
</«n.tagName»>
'''
}
}
There is a special IF to be used within templates:
def someHTML(Paragraph p) '''
<html>
<body>
«IF p.headLine != null»
<h1>«p.headline»</h1>
«ENDIF»
<p>
«p.text»
</p>
</body>
</html>
'''
Also a FOR statement is available:
def someHTML(List<Paragraph> paragraphs) '''
<html>
<body>
«FOR p : paragraphs»
«IF p.headLine != null»
<h1>«p.headline»</h1>
«ENDIF»
<p>
«p.text»
</p>
«ENDFOR»
</body>
</html>
'''
The for statement optionally allows to specify what to prepend (BEFORE), put in-between (SEPARATOR), and what to put at the end (AFTER) of all iterations. BEFORE and AFTER are only executed if there is at least one iteration. (SEPARATOR) is only added between iterations, that it is executed if there are at least two iterations.
Here is an example:
def someHTML(List<Paragraph> paragraphs) '''
<html>
<body>
«FOR p : paragraphs BEFORE '<div>' SEPARATOR '</div><div>' AFTER '</div>'»
«IF p.headLine != null»
<h1>«p.headline»</h1>
«ENDIF»
<p>
«p.text»
</p>
«ENDFOR»
</body>
</html>
'''
The template expression is of type CharSequence. It is automatically converted to String if that is the expected target type.
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. The following three rules are applied when the template is evaluated:
Although this algorithm sounds a bit complicated at first it behaves very intuitively. In addition the syntax coloring in Eclipse communicates this behavior.
The behavior is best described with a set of examples. The following table assumes a data structure of nested nodes.
class Template { |
node NodeName {} |
The indentation before node «n.name» will be skipped as it is relative to the opening mark of the template string and thereby not considered to be relevant for the output but only for readability of the template itself.
class Template { |
node Parent{ |
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.