Once you have installed Ant, open a command prompt in the examples directory, and type
ant
to build, and run the tests. It may be useful to look in the generated src directory to see the
macro expansions.
when
The when macro is probably the simplest macro you can write. It is like a limited if with no
else blocks. The idea is that code like
when (condition) {
doSomething();
}
is preprocessed by JSE to the Java source
if (condition) {
doSomething();
}
This is achieved by writing a when macro like this:
public syntax when {
case #{
when (?exp:expression)
?stmt:statement
}:
return #{
if (?exp) {
?stmt
}
};
}
The case statement can be thought of as a generalised Java switch/case for
pattern matching.
unless
In the same vein consider the unless macro,
which is the inverse of when. This
unless (condition) {
doSomething();
}
expands to
if (!(condition)) {
doSomething();
}
Note that this time, the macro uses the expression ?:expression rather than
?expr:expression since the variable name defaults to the constraint name -
so I can refer to ?expression in the return template.
using
The using macro allows you to dispense with
ungainly finally blocks that call close() on a resource. That is,
using (Reader r = new BufferedReader(new FileReader(filename))) {
doSomething(r.readLine());
}
is equivalent to:
{
Reader r = null;
try {
r = new BufferedReader(new FileReader(filename));
doSomething(r.readLine());
} finally {
if (r != null) {
r.close();
}
}
}
Notice how much shorter the code with the macro call is.
foreach
The foreach macro is more complicated.
Code like:
List l = ...
foreach (String s in l) {
doSomething(s);
}
expands to:
List l = ...
for(java.util.Iterator i = l.iterator(); i.hasNext(); ) {
String s = (String) i.next();
doSomething(s);
}
This example demonstrates the problem of variable capture: if the expansion is as shown above,
then a nested foreach block will create a name clash for the local variable i.
When hygiene is implemented in JSE, the preprocessor will take care of this clash by carefully renaming
all local variables. Until then, you need to do the renaming yourself, using the method genSym
(generate symbol) on the IdentifierFragment class.
assert
The assert macro provides a facility very like the
facility introduced in Java 1.4.
The differences are only in syntax, and the granularity of switching assertions on or off, the version presented
here only allows assertions to be either all on or all off. Code like:
assert(x == y, "x: " + x + ", y: " + y)
expands to (see the assertions spec above for details of why this particular expansion is used):
do {
if (!(examples.AssertionsStatus.ASSERTIONS_DISABLED || (x == y)))
throw new examples.AssertionError("x: " + x + ", y: " + y);
} while(false);
This example demonstrates the use of matching multiple patterns.
property
The property macro allows you to specify JavaBeans
properties. Code like:
class Bean implements Serializable {
public property String firstName = "Tom";
}
expands to:
class Bean implements Serializable {
private String firstName = "Tom";
public String getFirstName () { return firstName ; }
public void setFirstName (String x) { this.firstName = x ; }
}
It would be simple to extend the example to support read-only and write-only properties.
enum
The enum macro provides a simple facility for
enumerated types. Typical usage:
public enum Suit clubs, diamonds, hearts, spades;
This will create a class called Suit that has the enumerated constants
clubs, diamonds, hearts, and spades
each of type Suit. All generated enumerated type classes are Serializable
and Comparable.
The facility shown here is simpler than the one proposed by Sun
for introduction into Java 1.5. The major enhancements are the ability to add arbitrary fields and methods to an enum class, and
the ability make the enum type implement arbitrary interfaces. Also, the Sun proposal extends the
switch statement to allow switching on enumerated types - something we
can not achieve using JSE. (However, switch is best replaced with method dispatching on the enum.)