Post-translation step: cache constant methods in static final fields.
If a generated Java method is nullary (i.e. it has no parameters), it always returns the same value. Recall that the Java method represents an ACL2 function, and that we translate ACL2 functions to methods with the same arity. (This may no longer be the case in the future, if we introduce a way to generate Java code that accesses non-constant fields, but for now that is not supported.) Thus, we can improve efficiency by caching that value into a static final field that the method returns. If the method is never called, this actually decreases efficiency, because the value has to be calculated at class initialization time. However, if the method is called once, then the performance should be about the same, while if the method is called multiple times, the performance should be better. Note also that a JIT would presumably inline the method call.
We achieve this optimization (it generally is an optimization, but see above for a discussion) as a post-translation step that operates as follows. For a nullary method whose body cosists of just a return statement whose expression has no method calls, we add a static final field initialized with the return expression, and we replace the return expression in the method with a reference to the new static final field. Java allows a field and a method to have the same name [JLS14:8.2], but discourages that as a matter of style; since our Java code is generated, it seems acceptable to violate that stylistic guide in favor of simplicity, but we may revisit this decision in the future.
The restriction that the body of the method is a single return expression makes it possible to move that expression into the field declaration, which needs an expression as an initializer, and cannot take a block. (This restriction could be lifted via a more complex code transformation, which is a possible future extension.)
The restriction that the return expression calls no method avoids class initialization issues. According to [JLS14:12.4.1], a class's static initializer(s) (if any) and static final fields (if any) are initialized (i.e. executed) in textual order. If the initializer of a static final field contained a method call, that method call could require the initialization of a static final field that comes later in a class, causing a compilation error. (This restriction could be lifted via a more complex code transformation, which is a possible future extension.)
While other post-translation steps operate on individual method bodies, this post-translation step operates on a class level. It is applied to all the nested classes in the main Java class: it is the nested classes that contain the methods to be optimized.