Generate Code using Java Annotation Processor

[Updated: Mar 29, 2017, Created: Jan 1, 2016]

In this example, we are going to show how we can use the processor API to generate code.

At times, there might be large number of fields with some java class. Mandatory fields must be initialized via constructor. For optional fields we have choice to create various overloaded constructors or create setters. The problem with setters is, the object is in inconsistent state during setter calls. Conceptually fields initializing must be performed during construction time but for large number of fields that becomes very messy. Builder Design Pattern is solution for this problem. This tutorial is not going to explain the design pattern but to generate the boilerplate code associated with it.

In this example project we are going to define two annotations. @AutoImplement and @Mandatory. If @AutoImplement is specified on an interface, our annotation processor will generate the implementation of that interface with fields extracted from the getters. If a particular getter is annotated with @Mandatory then the corresponding field will be treated as mandatory field, otherwise optional field. We will also generated the Builder class as public static nested class of the target class.

In this example too, we are going to create two projects. The first one will create the processor and second one will be the client code tagging the interfaces with our annotations

For the both projects, steps are exactly same as previous ones.

The Processor Project


Dependencies and Technologies Used :

  • JDK 1.8
  • Maven 3.0.4

Generate Code Processor Select All Download
  • generate-code-processor
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • annotation
                • processor
        • resources
          • META-INF
            • services

The Client Project


Dependencies and Technologies Used :

  • com.logicbig.examples:generate-code-processor 1.0-SNAPSHOT
  • JDK 1.8
  • Maven 3.0.4

Generate Code Client Select All Download
  • generate-code-client
    • src
      • main
        • java
          • com
            • logicbig
              • example

On compiling the client project we will see the new class User been generated in {project-root}/target/generated-sources/annotations/com/logicbig/example/User.java. Here's the generated class:

package com.logicbig.example;


public class User implements IUser {
    private java.lang.String firstName;
    private java.lang.String lastName;
    private java.time.LocalDate dateOfBirth;
    private java.lang.String placeOfBirth;
    private java.lang.String phone;
    private java.lang.String address;

    private User(java.lang.String firstName,
                            java.lang.String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public java.lang.String getFirstName() {
        return this.firstName;
    }

    public java.lang.String getLastName() {
        return this.lastName;
    }

    public java.time.LocalDate getDateOfBirth() {
        return this.dateOfBirth;
    }

    public java.lang.String getPlaceOfBirth() {
        return this.placeOfBirth;
    }


    public java.lang.String getPhone() {
        return this.phone;
    }

    public java.lang.String getAddress() {
        return this.address;
    }

    public static class UserBuilder {
        private java.lang.String firstName;
        private java.lang.String lastName;
        private java.time.LocalDate dateOfBirth;
        private java.lang.String placeOfBirth;
        private java.lang.String phone;
        private java.lang.String address;

        private UserBuilder(java.lang.String firstName,
                                    java.lang.String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }

        public UserBuilder dateOfBirth(java.time.LocalDate dateOfBirth) {
            this.dateOfBirth = dateOfBirth;
            return this;
        }

        public UserBuilder placeOfBirth(java.lang.String placeOfBirth) {
            this.placeOfBirth = placeOfBirth;
            return this;
        }

        public UserBuilder phone(java.lang.String phone) {
            this.phone = phone;
            return this;
        }

        public UserBuilder address(java.lang.String address) {
            this.address = address;
            return this;
        }

        public static UserBuilder create(java.lang.String firstName,
                                java.lang.String lastName) {
            return new UserBuilder(firstName, lastName);
        }

        public User build() {
            User a = new User(firstName, lastName);
            a.dateOfBirth = dateOfBirth;
            a.placeOfBirth = placeOfBirth;
            a.phone = phone;
            a.address = address;
            return a;
        }
    }
}

We can still improve our processor by generating equals(), hashCode() and toString() methods. We have to define a new annotation say EqualityField , to be annotated in the client interface methods, to specify the fields on which our equals/hashCode method will be based on.

Regarding writing dynamic code and embedding that in strings is the most time consuming part. I tried to simplify that by creating JClass and JMethod. We also have a non standard java library: CodeModel, may be you want to try that.

See Also