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.)