Skip to content

analytically/innerbuilder

Repository files navigation

InnerBuilder

IntelliJ IDEA plugin that adds a Builder action to the Generate menu (Alt+Insert) which generates an inner builder class as described in Effective Java by Joshua Bloch.

Works with IntelliJ IDEA and Android Studio 2019 and later.

screenshot

Why Use the Builder Pattern?

The Builder pattern is recommended when:

  • Classes have many constructor parameters (4+)
  • Many parameters are optional
  • You want immutable objects with readable construction code
  • You need to validate parameters before object creation

Instead of telescoping constructors or JavaBeans setters, you get:

JavaBean bean = JavaBean.newBuilder()
    .foo("required")
    .bar("optional")
    .qux(42)
    .build();

What About Java Records?

Java Records (introduced in Java 16) are great for simple data carriers, but they don't replace the Builder pattern:

  • No built-in builder support - Records don't generate builders automatically
  • Public constructors only - You can't force clients to use a builder with records
  • Limited validation - Compact constructors help, but builders offer more control over construction order
  • Named parameters - Builders let you name each value; record constructors require positional arguments

Use Records when: You have simple, immutable data containers with few fields. Use Builder pattern when: You have many fields, optional parameters, complex validation, or need a fluent API.

You can even combine both - create a Builder for a Record when construction is complex:

public record Person(String firstName, String lastName, int age, String email) {
    public static Builder builder() {
        return new Builder();
    }
    
    public static final class Builder {
        // ... generated by InnerBuilder
    }
}

Example Output

Given a class with fields:

public class JavaBean {
    private final String foo;
    private String bar;
    private int qux;
    private Double x, y;
}

The plugin generates:

public class JavaBean {
    private final String foo;
    private String bar;
    private int qux;
    private Double x, y;

    private JavaBean(Builder builder) {
        foo = builder.foo;
        bar = builder.bar;
        qux = builder.qux;
        x = builder.x;
        y = builder.y;
    }

    public static Builder newBuilder() {
        return new Builder();
    }

    public static final class Builder {
        private String foo;
        private String bar;
        private int qux;
        private Double x;
        private Double y;

        private Builder() {
        }

        public Builder foo(String val) {
            foo = val;
            return this;
        }

        public Builder bar(String val) {
            bar = val;
            return this;
        }

        public Builder qux(int val) {
            qux = val;
            return this;
        }

        public Builder x(Double val) {
            x = val;
            return this;
        }

        public Builder y(Double val) {
            y = val;
            return this;
        }

        public JavaBean build() {
            return new JavaBean(this);
        }
    }
}

Installation

From JetBrains Marketplace (Recommended)

  1. Open Settings/PreferencesPlugins
  2. Select Marketplace tab
  3. Search for innerbuilder
  4. Click Install and restart IDE

Manual Installation

  1. Download innerbuilder.jar from releases
  2. Open Settings/PreferencesPlugins
  3. Click ⚙️ → Install Plugin from Disk...
  4. Select the downloaded JAR file
  5. Restart IDE

Usage

  1. Place cursor inside a class
  2. Press Shift+Alt+B or Alt+Insert (Generate menu)
  3. Select Builder...
  4. Choose fields to include
  5. Configure options (see below)
  6. Click OK

Updating Existing Builders

When generating a builder for a class that already has one, the plugin will:

  • ✅ Add missing fields to the Builder
  • ✅ Add missing builder methods
  • ✅ Update the private constructor
  • ❌ Never remove existing fields or methods (safe for customizations)

Options

Option Description Example
Generate builder methods for final fields Include final fields in builder (requires constructor params) Builder(String foo)
Generate static builder method Add static factory method to create Builder MyClass.newBuilder()
Static builder naming Choose naming convention for static method newBuilder(), builder(), newMyClass(), newMyClassBuilder()
Builder method location Place static method in parent class or Builder Inside parent class / Inside Builder
Generate builder copy constructor Add constructor/method to copy from existing instance newBuilder(existingObject)
Use 'with...' notation Prefix builder methods with 'with' withName(String name)
Use 'set...' notation Prefix builder methods with 'set' setName(String name)
Add JSR-305 @Nonnull annotation Add nullability annotations @Nonnull public Builder withName(@Nonnull String name)
Add PMD suppression Suppress PMD.AvoidFieldNameMatchingMethodName @SuppressWarnings("PMD...")
Add Javadoc Generate documentation for builder /** Sets the name... */
Use field names in setter Use field name as parameter name withName(String name) vs withName(String val)

Configuration Persistence

All options are remembered between invocations. Settings are stored per-project in IntelliJ's PropertiesComponent.

Android Studio Support

The plugin fully supports Android Studio, including:

  • Field name prefixes (e.g., mFieldNamefieldName() method)
  • Code style settings are respected
  • Works with Kotlin classes containing Java interop

Copy Constructor Behavior

When "Generate builder copy constructor" is enabled:

  • Public fields: Accessed directly (copy.fieldName)
  • Private fields with getter: Uses getter (copy.getFieldName())
  • Private fields without getter: Accessed directly (assumes same package)
// With static builder method enabled
public static Builder newBuilder(MyClass copy) {
    Builder builder = new Builder();
    builder.publicField = copy.publicField;
    builder.privateField = copy.getPrivateField();
    return builder;
}

// Without static builder method
public Builder(MyClass copy) {
    this.publicField = copy.publicField;
    this.privateField = copy.getPrivateField();
}

Keyboard Shortcuts

Action Windows/Linux macOS
Generate Builder Shift+Alt+B ⇧⌥B
Generate Menu Alt+Insert ⌘N

Compatibility

IDE Minimum Version
IntelliJ IDEA Community 2019.1
IntelliJ IDEA Ultimate 2019.1
Android Studio 3.4

Building from Source

Prerequisites

  • JDK 11 or later
  • Maven 3.6+

Build Steps

# Download IntelliJ IDEA Community Edition for compilation
./prepare-build.sh

# Build the plugin
mvn package

# Output: target/innerbuilder.jar

Running Tests

mvn test

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

Licensed under the Apache License, Version 2.0.

Copyright 2013-2025 Mathias Bogaert

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Support

Acknowledgments

Contributors 9