LWO #002: Annotations are just modifiers, array access curiosity, faux code generators and more

Marcio Endo
November 19, 2022

Hi, welcome to the second edition of Last Week in Objectos (LWO).

In this edition I will discuss:

Let's begin.

Annotations are just modifiers (grammatically speaking)

I didn't know that, grammatically (syntactically?) speaking, annotations are just modifiers to the Java language. In other words, I have always thought that the only way to annotate, say a method, would be:

@Override
public final String toString() {
  return "Annotations are just modifiers";
}

But, in fact, you can write:

public @Override final String toString() ...

Or, if you prefer:

public final @Override String toString() ...

I always thought that the last two examples would fail to compile. But they do not; all of the three declarations are syntactically correct.

Additionally, all of the three declarations are equivalent. Well, there is a small caveat in the last example regarding type annotations, which we'll look into at another time.

I found out about it when reading the grammar production of method declarations:

MethodDeclaration:
  {MethodModifier} MethodHeader MethodBody

It means that a method declaration can contain zero or more MethodModifier. The MethodModifier production is defined as:

MethodModifier:
  (one of)
  Annotation public protected private
  abstract static final synchronized native strictfp

So any of the modifiers can appear in any order. Cool!

An Array Access Expression curiosity

Array access expressions are expressions to, well, access a component of an array.

A simple example can be:

String[] a = { "array", "access" };
System.out.println(a[1]);

The argument to the println method, i.e. the a[1], is an array access expression.

The following is wrong and does not compile:

System.out.println(this[1]);

But why does it not compile? Well, that's what is curious to me. I thought it failed to compile simply because the "syntax is invalid". But that is not exactly what happens in this case.

Let's try to compile and run the following program:

public class Test {
  public static void main(String[] args) {
    new Test().execute();
  }
  
  public void execute() {
    System.out.println(this[1]);
  }
}

When I try to run it:

$ java Test.java
Test.java:7: error: array required, but Test found
  System.out.println(this[1]);
                         ^
1 error
error: compilation failed

It fails to compile because this does not evaluate to an array type. So, in some ways, the syntax itself is valid. But, as this resolves to a non-array type, it fails to compile.

This is expected as it is defined in the language specification. The ArrayAccess production is given by the following:

ArrayAccess:
  ExpressionName [ Expression ]
  PrimaryNoNewArray [ Expression ]
  ArrayCreationExpressionWithInitializer [ Expression ]

We are interested in the second form, the one with PrimaryNoNewArray. PrimaryNoNewArray is defined as:

Literal
ClassLiteral
this
TypeName . this
( Expression )
ClassInstanceCreationExpression
FieldAccess
ArrayAccess
MethodInvocation
MethodReference

So the following (wrong) examples:

1234[0];
Object.class[1];
this[2];
Outer.this[3];

All fail to compile. But not because the "syntax is invalid". Instead, they fail to compile because the part to left of the brackets do not evaluate to an array type.

Faux code generators

I am creating Objectos Code because I write a lot of code generators. But parts of Objectos Code itself might benefit of having them being generated. What to do in this case?

Well, I write a plain "faux" (ad-hoc?) code generator. It looks a bit like this:

import static java.lang.System.out;

public class ByteCodeFauxGenerator {
  private int value = -1;
  
  public static void main(String[] args) {
    var gen = new ByteCodeFauxGenerator();

    gen.execute();
  }

  public final void execute() {
    comment("class");

    value("CLASS");
    value("MODIFIER");
    value("IDENTIFIER");

    comment("statements");

    value("LOCAL_VARIABLE");
    value("RETURN_STATEMENT");
  }  

  private void comment(String string) {
    out.println();
    out.println("// " + string);
    out.println();
  }

  private void value(String string) {
    out.println("static final int " + string + " = " + value-- + ";");
  }  
}

This code resides in the src/test/java directory of the project. It has a main method, so I can just run it. It prints:

// class

static final int CLASS = -1;
static final int MODIFIER = -2;
static final int IDENTIFIER = -3;

// statements

static final int LOCAL_VARIABLE = -4;
static final int RETURN_STATEMENT = -5;

Then I copy this output and paste it into the actual class, located in src/main/java:

final class ByteCode {
  // class

  static final int CLASS = -1;
  static final int MODIFIER = -2;
  static final int IDENTIFIER = -3;

  // statements

  static final int LOCAL_VARIABLE = -4;
  static final int RETURN_STATEMENT = -5;

  private ByteCode() {}
}

I call it a "faux" code generator because the process is manual. Even though it is manual, it is still effective.

Last week in Objectos Code

And here is a summary of the work I have done in Objectos Code.

Lambda inclusion support

To generate a class with a single field you write the following in Objectos Code:

_class(
  id("Example"),
  field(_int(), id("a"))
);

It generates the following:

class Example {
  int a;
}

What if you don't know beforehand the number and names of the fields to be generated?

To this use-case, Objectos Code offers the include directive:

public class IncludeExample extends JavaTemplate {
  private final List<String> fieldNames = List.of("a", "b", "c");

  @Override
  protected final void definition() {
    _class(
      id("Example"),
      include(this::generateFields)
    );
  }

  private void generateFields() {
    for (var fieldName : fieldNames) {
      field(_int(), id(fieldName));
    }
  }
}

Which should generate:

class Example {
  int a;
  
  int b;
  
  int c;
}

Remember that Objectos Code is a pure Java template. So the previous example is equivalent to the following string-based template:

class Example {
  {{#fieldNames}
  int {{.}};
  {{/fieldNames}
}

Constructor declarations, assignment expressions, and field access expressions

I have started the implementation of constructor declarations. The following Objectos Code:

_class(
  id("Foo"),
  field(_final(), _int(), id("a")),
  constructor(
    param(_int(), id("a"),
    assign(n(_this(), "a"), n("a"))
  )
);

Generates the following Java code:

class Foo {
  final int a;
  
  Foo(int a) {
    this.a = a;
  }
}

More enum declarations

I have also continued the work on enum declarations. Enum class declarations are currently required in Objectos HTML.

The following Objectos Code:

var test = PackageName.of("test");
var iface = ClassName.of(test, "Iface");

_enum(
  _public(), id("Test"), _implements(iface),
  enumConstant(id("A"), s("a")),
  enumConstant(id("B"), s("b")),
  field(_private(), _final(), t(String.class), id("value")),
  constructor(
    _private(),
    param(t(String.class), id("value")),
    assign(n(_this(), "value"), n("value"))
  ),
  method(
    annotation(Override.class),
    _public(), _final(), t(String.class), id("toString"),
    _return(n("value"))
  )
);

Generates:

public enum Test implements test.Iface {
  A("a"),

  B("b");

  private final java.lang.String value;

  private Test(java.lang.String value) {
    this.value = value;
  }

  @java.lang.Override
  public final java.lang.String toString() {
    return value;
  }
}

Until the next edition

So that's it for today. I hope you've enjoyed reading.

You can find the source code in this GitHub repository.

Please send me an e-mail if you have comments, questions or corrections regarding this post.

If you liked this content you can follow me on Twitter.