Code Generation

Trisha Gee

A few weeks back we published a blog post and video about Code Completion. Now we’re going to look at Code Generation, which compliments the completion features so nicely that in the past we put both sets of features into a single video.

Code Generation is a really helpful feature in IntelliJ IDEA, particularly for Java programmers. Although Java as a language is steadily reducing the amount of boilerplate and typing that developers need to do, there are still plenty of common patterns of code that we simply shouldn’t have to type character by character. Indeed, in IntelliJ IDEA, we do not have to. This video and blog post show why not.

This blog post covers the same material as the video. The goal of the blog is to provide an easy way for people to skim the content quickly if they prefer reading to watching, and to give the reader/watcher code samples and links to additional information.

First we’ll explore some of the common ways to generate code that we’ve already seen in other videos. Using ⌘N on MacOS or Alt+Insert on Windows and Linux, in the project window we can create a new package. IntelliJ IDEA will use the full package declaration to create all the necessary folders to match this package name. We can invoke New again with the same keyboard shortcut and use it to create a new Java class. Note that if we want to change the code that’s generated here, we can alter the file and code templates.

IntelliJ IDEA can create a number of different types of classes, including the new Record preview feature in Java 14. Let’s create an interface. IntelliJ IDEA creates a new Java file with the correct code to declare this as a new interface.

public interface Car {
}

We can continue creating classes from the project window if we want, but it’s often more convenient to do from inside the code. If we press Alt+Enter on the interface declaration (to invoke intention actions), IntelliJ IDEA suggests an option to implement this interface.

Screenshot 2020-05-07 at 16.12.49

We can enter our own name for the implementation of this interface, let’s say “Fiesta”. IntelliJ IDEA creates a Java class file that implements this interface and opens it in the editor.

public class Fiesta implements Car {
}

Let’s look at generating new code inside these classes. Using ⌘N, or Alt+Insert, again, we can choose to generate a constructor for this class. In this case IntelliJ IDEA creates a default constructor with no parameters.
public class Fiesta implements Car {
    public Fiesta() {
    }
}

If we add a parameter into the constructor, we don’t have to type all of the code to create a field for this and to assign the parameter to the field. We can press Alt+Enter and have IntelliJ IDEA generate the field and the assignment for us.
    private final int numberOfDoors;

    public Fiesta(int numberOfDoors) {
        this.numberOfDoors = numberOfDoors;
    }

If we use ⌘N or Alt+Insert, again, we can see the option to create getters for the fields in this class. Let’s not do this right now. There’s usually more than one way to do things in IntelliJ IDEA. We can also highlight the field by using F2, and show the suggestions for it by pressing Alt+Enter. One of the suggestions is to create a getter for the field. Both times the only suggestions were for a getter for the field, not a setter and getter.

Delete the final keyword, try again and see what happens. The actions available in IntelliJ IDEA are context sensitive. Now the field can be updated, IntelliJ IDEA also suggests creating a setter for it. If we open the Generate menu (with ⌘N or Alt+Insert) we also see this too has the option to generate a setter. Let’s generate a getter and setter for our numberOfDoors field. IntelliJ IDEA provides a default template for both getter and setter. For the setter, we could optionally choose to use the Builder pattern instead. When we do this, IntelliJ IDEA generates a standard getter, and a builder-style setter.

    private int numberOfDoors;

    public Fiesta(int numberOfDoors) {
        this.numberOfDoors = numberOfDoors;
    }

    public int getNumberOfDoors() {
        return numberOfDoors;
    }

    public Fiesta setNumberOfDoors(int numberOfDoors) {
        this.numberOfDoors = numberOfDoors;
        return this;
    }

Let’s delete the setter, since it’s generally better to have immutable state, and look at other ways IntelliJ IDEA can generate code for us.

The way IntelliJ IDEA works is designed to help us stay in the flow while coding. We often want to sketch out ideas for how we want a class or API to look, rather than manually creating a lot of files up front. IntelliJ IDEA supports this, if we refer to a class that doesn’t exist yet, we can use Alt+Enter and get IntelliJ IDEA to create something that makes the code compile.

public class Fiesta implements Car {
    private final int numberOfDoors;
    private Fuel fuel;
    // rest of class...
}

Press Alt+Enter on Fuel and create an enum to represent the fuel type in this package. IntelliJ IDEA creates a valid enum class that matches what our code was expecting.
public enum Fuel {
}

When we navigate back to our Fiesta class it’s now compiling.

We need to assign a value to this field somehow. Using Alt+Enter on “fuel” we can get IntelliJ IDEA to generate a new parameter on the constructor, and assign the parameter value to the field.

    public Fiesta(int numberOfDoors, Fuel fuel) {
        this.numberOfDoors = numberOfDoors;
        this.fuel = fuel;
    }

Alternatively we can use ⌘N or Alt+Insert to bring up the generate menu, and we can choose to generate a new constructor. We can select all existing fields as parameters to this constructor. IntelliJ IDEA will create a second constructor which initialises both fields.

By now we’ve seen that IntelliJ IDEA can generate a lot of basic code for us. Let’s bring up the generate menu again (⌘N or Alt+Insert) to see what else it can do.

We can generate an equals and a hashcode method for the class. It’s a good idea to generate both of these at the same time as these two methods are related. IntelliJ IDEA has a number of different templates so we can generate these methods in one of a number of standard styles. We can even see and edit, or add, these templates so our code always uses whichever standard we choose. Let’s use the default for this example. We can select which fields to include in the equals method, and in the hashcode method. Telling IntelliJ IDEA which fields will never be null simplifies the code it generates. Now we can see standard generated code for equals and hashcode for this class.

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Fiesta fiesta = (Fiesta) o;
        return numberOfDoors == fiesta.numberOfDoors &&
                fuel == fiesta.fuel;
    }

    @Override
    public int hashCode() {
        return Objects.hash(numberOfDoors, fuel);
    }

We can also generate a toString method, again we have a number of templates to choose from, and we can define our own. Let’s have all the fields appear in toString.
    @Override
    public String toString() {
        return "Fiesta{" +
                "numberOfDoors=" + numberOfDoors +
                ", fuel=" + fuel +
                '}';
    }

Now we have a full domain object with a constructor, getter, toString, equals and hashcode, and we had to do very little typing to create all of this code. IntelliJ IDEA generated most of it for us.

Let’s go back to our Car interface. Let’s define a new method on this interface.

public interface Car {
    void drive();
}

When we create a method on an interface, we know all the classes that implement this interface should also implement this method. Let’s go to the implementation of this interface, the Fiesta class. It doesn’t compile any more, because it doesn’t implement the new drive method. We can fix this in a number of ways. We could press Alt+Enter on the error itself, and IntelliJ IDEA will suggest generating the missing method as one way to fix the problem.

Screenshot 2020-05-07 at 16.35.08

When we pick this, the method is created on this class with the correct signature, and sensible defaults to ensure the code compiles.

    @Override
    public void drive() {

    }

For an alternative approach, we could also use Generate (⌘N or Alt+Insert) and select Implement Methods, or we could use the shortcut, Ctrl+I, that will take us directly to this feature. This will show us a list of methods on the interface that need implementing.

Once the outline of the method is created, it’s up to us to implement the details, the actual business logic.

We’ve seen generating methods on the subclass from a signature on the interface, what about the other way around? We can annotate a method on this class with @Override.

    @Override
    public int getNumberOfDoors() {
        return numberOfDoors;
    }

The IDE will tell us that this is invalid, as there’s no method matching this signature on an interface or superclass. Using Alt+Enter on this error provides a suggestion that we can pull this method numberOfDoors up to the Car interface. Let’s do this. The error goes away, the gutter icon shows us this method now implements a method from the Car interface. If we navigate to the super method, we can see IntelliJ IDEA generated the method signature on the Car interface, which is what we wanted.
public interface Car {
    void drive();
    int getNumberOfDoors();
}

What else can we generate? We can use the Navigate to test shortcut (⇧⌘T or Ctrl+Shift+T) from our Fiesta class. Since we don’t have a test for this class yet, IntelliJ IDEA offers to create one for us. We can pick from a wide number of different testing frameworks. We’re going to pick JUnit 4 since that’s the framework we’re using in this project. We could generate additional boilerplate like setup and teardown too if this is appropriate. We can use Generate (⌘N or Alt+Insert) again, and choose to generate a test method for this class. We should then write the code that tests some aspect of the Fiesta class.
public class FiestaTest {
    @Test
    public void shouldBeAbleToDriveACar() {
        // TODO: write test
    }
}

Let’s see some more ways of getting IntelliJ IDEA to generate code for us. We saw earlier that IntelliJ IDEA is designed to support us while we code, letting us stay in the flow writing the code the way we think it should look. Code suggestions and completion help where appropriate, yet sometimes we know what we want to write even if it’s not supported yet. IntelliJ IDEA provides context sensitive suggestions with errors and can generate code that makes our code compile.
    public void shouldBeAbleToDriveACar() {
        new Fiesta(4, PETROL);
    }

(Note: will not compile)

We want to create a new enum constant for the value PETROL in the Fuel enumeration. If we press Alt+Enter on this value, IntelliJ IDEA offers the suggestion of creating the enum constant

Screenshot 2020-05-07 at 16.47.25

If we look at the Fuel class, we can see the petrol constant was declared as expected.

public enum Fuel {
    PETROL
}

If we’re doing Test Driven Development, we might call methods on an object that don’t yet exist.
    @Test
    public void shouldBeAbleToDriveACar() {
        Fiesta fiesta = new Fiesta(4, Fuel.PETROL);
        fiesta.park();
    }

(Note: will not compile)

If we do this, we can use Alt+Enter on park() to see what IntelliJ IDEA suggests. IntelliJ IDEA identifies that we can create a method that matches this signature either on the Fiesta class itself, or the Car interface. Let’s create it on Fiesta for simplicity since we don’t know yet if it’s needed on the interface.

By now we’ve covered a whole range of ways IntelliJ IDEA can generate code for us. These are all common use cases that, if we get used to using them while coding, can really decrease the amount of time we spend typing common code, and free us up to concentrate on the business problem we’re trying to solve. There are more ways to generate code in IntelliJ IDEA, some are for very specific use cases. For example, we can use the Delegation pattern by getting IntelliJ IDEA to generate methods that delegate the call to a delegate inside this class.

Now let’s put some of these techniques together to show how to use the different types of code generation together to do the heavy lifting of creating code. We can create simple classes that make the code compile, as we use them.

public class Fiesta implements Car {
    private final int numberOfDoors;
    private Fuel fuel;
    List<Wheel> wheels
    // rest of class...
}

(Note: will not compile)

Pressing Alt+Enter on Wheel gives us the option to have the Wheel class created for us.

public class Wheel {
}

Back in Fiesta, we can use Complete Current Statement (⇧⌘⏎ or Ctrl+Shift+Enter), to finish off our line and put the caret in the correct place.
public class Fiesta implements Car {
    private final int numberOfDoors;
    private Fuel fuel;
    List<Wheel> wheels = List.of();
    // rest of class...
}

We can use complete current statement again to create a method’s curly braces and get us ready to create the method content. We can even use complete current statement to generate the outline of a for loop, and then we just have to fill in the details, which code completion can help us with.

However, that’s still quite a lot of typing for a common code pattern. Let’s look at an alternative. We can use live templates. There are a number of live templates to iterate over different types of data structures. For a list like our wheels list, we can type iter and press tab or enter, and IntelliJ IDEA will create all the code to iterate over our wheels list. Then we could use one of the sout templates to print something to standard out.

We see that by using code generation in this way, and combining it with code completion and live templates, the code is compiling almost all the time. Any time there’s an error we get the IDE to automatically fix the problem in the simplest way, and we grow our application this way.

See also:

Comments below can no longer be edited.

2 Responses to Code Generation

  1. Andrei Ivanov says:

    May 21, 2020

    A bit off-topic, since the notes with “will not compile” triggered this…
    What I really miss from my Eclipse usage is the fact that I can have some classes with compilation errors but still it is able to compile and run other classes in that project.

    That’s of huge help when refactoring code and you get a lot of test classes with compilation errors and you can fix and run them one by one.

Subscribe

Subscribe for updates