140

I have recently started to read Effective Java by Joshua Bloch. I found the idea of the Builder pattern [Item 2 in the book] really interesting. I tried to implement it in my project but there were compilation errors. Following is in essence what I was trying to do:

The class with multiple attributes and its builder class:

public class NutritionalFacts {
    private int sodium;
    private int fat;
    private int carbo;

    public class Builder {
        private int sodium;
        private int fat;
        private int carbo;

        public Builder(int s) {
            this.sodium = s;
        }

        public Builder fat(int f) {
            this.fat = f;
            return this;
        }

        public Builder carbo(int c) {
            this.carbo = c;
            return this;
        }

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

    private NutritionalFacts(Builder b) {
        this.sodium = b.sodium;
        this.fat = b.fat;
        this.carbo = b.carbo;
    }
}

Class where I try to use the above class:

public class Main {
    public static void main(String args[]) {
        NutritionalFacts n = 
            new NutritionalFacts.Builder(10).carbo(23).fat(1).build();
    }
}

I am getting the following compiler error:

an enclosing instance that contains effectivejava.BuilderPattern.NutritionalFacts.Builder is required NutritionalFacts n = new NutritionalFacts.Builder(10).carbo(23).fat(1).build();

I do not understand what the message means. Please explain. The above code is similar to the example suggested by Bloch in his book.

Adam Liss
  • 47,594
  • 12
  • 108
  • 150
Swaranga Sarma
  • 13,055
  • 19
  • 60
  • 93
  • 1
    possible duplicate of [An enclosing instance that contains is required](http://stackoverflow.com/questions/4297857/an-enclosing-instance-that-contains-my-reference-is-required) – Joshua Taylor Sep 26 '13 at 12:41

12 Answers12

179

Make the builder a static class. Then it will work. If it is non-static, it would require an instance of its owning class - and the point is not to have an instance of it, and even to forbid making instances without the builder.

public class NutritionFacts {
    public static class Builder {
    }
}

Reference: Nested classes

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • 34
    And, in fact, `Builder` is `static` in the example in the book (page 14, line 10 in the 2nd Edition). – Powerlord Feb 15 '11 at 18:01
30

You should make the Builder class as static and also you should make the fields final and have getters to get those values. Don't provide setters to those values. In this way your class will be perfectly immutable.

public class NutritionalFacts {
    private final int sodium;
    private final int fat;
    private final int carbo;

    public int getSodium(){
        return sodium;
    }

    public int getFat(){
        return fat;
    }

    public int getCarbo(){
        return carbo;
    }

    public static class Builder {
        private int sodium;
        private int fat;
        private int carbo;

        public Builder sodium(int s) {
            this.sodium = s;
            return this;
        }

        public Builder fat(int f) {
            this.fat = f;
            return this;
        }

        public Builder carbo(int c) {
            this.carbo = c;
            return this;
        }

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

    private NutritionalFacts(Builder b) {
        this.sodium = b.sodium;
        this.fat = b.fat;
        this.carbo = b.carbo;
    }
}

And now you can set the properties as follows:

NutritionalFacts n = new NutritionalFacts.Builder().sodium(10).carbo(15).
fat(5).build();
Dónal
  • 185,044
  • 174
  • 569
  • 824
Raj Hassani
  • 1,577
  • 1
  • 19
  • 26
  • Why not just make the NutritionalFacts fields public? They're already final, and it would still be immutable. – nikodaemus Mar 12 '18 at 18:00
  • `final` fields make sense only if the fields are always needed during initialization. If not, then the fields shouldn't be `final`. – Piotrek Hryciuk Jan 28 '19 at 13:52
14

To generate an inner builder in Intellij IDEA, check out this plugin: https://github.com/analytically/innerbuilder

analytically
  • 223
  • 4
  • 4
12

You are trying access a non-static class in a static way. Change Builder to static class Builder and it should work.

The example usage you give fails because there is no instance of Builder present. A static class for all practical purposes is always instantiated. If you don't make it static, you'd need to say:

Widget = new Widget.Builder(10).setparm1(1).setparm2(3).build();

Because you would need to construct a new Builder every time.

Michael K
  • 3,297
  • 3
  • 25
  • 37
8

You need to declare the Builder inner class as static.

Consult some documentation for both non-static inner classes and static inner classes.

Basically the non-static inner classes instances cannot exist without attached outer class instance.

Grzegorz Oledzki
  • 23,614
  • 16
  • 68
  • 106
5

Once you've got an idea, in practice, you may find lombok's @Builder much more convenient.

@Builder lets you automatically produce the code required to have your class be instantiable with code such as:

Person.builder()
  .name("Adam Savage")
  .city("San Francisco")
  .job("Mythbusters")
  .job("Unchained Reaction")
 .build(); 

Official documentation: https://www.projectlombok.org/features/Builder

torina
  • 3,825
  • 2
  • 26
  • 31
4

This mean that you cant create enclose type. This mean that first you have to cerate a instance of "parent" class and then from this instance you can create nested class instances.

NutritionalFacts n = new NutritionalFacts()

Builder b = new n.Builder(10).carbo(23).fat(1).build();

Nested Classes

MBH
  • 16,271
  • 19
  • 99
  • 149
3

The Builder class should be static. I don't have time right now to actually test the code beyond that, but if it doesn't work let me know and I'll take another look.

Shaun
  • 2,446
  • 19
  • 33
0

I personally prefer to use the other approach, when you have 2 different classes. So you don't need any static class. This is basically to avoid write Class.Builder when you has to create a new instance.

public class Person {
    private String attr1;
    private String attr2;
    private String attr3;

    // package access
    Person(PersonBuilder builder) {
        this.attr1 = builder.getAttr1();
        // ...
    }

    // ...
    // getters and setters 
}

public class PersonBuilder (
    private String attr1;
    private String attr2;
    private String attr3;

    // constructor with required attribute
    public PersonBuilder(String attr1) {
        this.attr1 = attr1;
    }

    public PersonBuilder setAttr2(String attr2) {
        this.attr2 = attr2;
        return this;
    }

    public PersonBuilder setAttr3(String attr3) {
        this.attr3 = attr3;
        return this;
    }

    public Person build() {
        return new Person(this);
    }
    // ....
}

So, you can use your builder like this:

Person person = new PersonBuilder("attr1")
                            .setAttr2("attr2")
                            .build();
fingerprints
  • 2,751
  • 1
  • 25
  • 45
0

As many already stated here you need to make the class static. Just small addition - if you want, there is a bit different way without static one.

Consider this. Implementing a builder by declaring something like withProperty(value) type setters inside the class and make them return a reference to itself. In this approach, you have a single and an elegant class which is a thread safe and concise.

Consider this:

public class DataObject {

    private String first;
    private String second;
    private String third;

    public String getFirst(){
       return first; 
    }

    public void setFirst(String first){
       this.first = first; 
    }

    ... 

    public DataObject withFirst(String first){
       this.first = first;
       return this; 
    }

    public DataObject withSecond(String second){
       this.second = second;
       return this; 
    }

    public DataObject withThird(String third){
       this.third = third;
       return this; 
    }
}


DataObject dataObject = new DataObject()
     .withFirst("first data")
     .withSecond("second data")
     .withThird("third data");

Check it out for more Java Builder examples.

Johnny
  • 14,397
  • 15
  • 77
  • 118
0

You need to change Builder class to static class Builder. Then it will work fine.

-1

The other solutions double the memory allocation to instantiate the object. The following solution does not have that problem.

public class NutritionalFacts{

    private int sodium;
    private int fat;
    private int carbo;

    private NutritionalFacts(){}

    public int getSodium(){ return sodium;}

    public int getFat(){ return fat;}

    public int getCarbo(){ return carbo;}

    public static class Builder{
        private NutritionalFacts nutrionalFacts;

        public Builder(){
           nutrionalFacts = new NutritionalFacts();
        }

        public Builder sodium(int s){
            nutrionalFacts.sodium = s;
            return this;
        }

        public Builder fat(int f){
            nutrionalFacts.fat = f;
            return this;
        }

        public Builder carbo(int c){
            nutrionalFacts.carbo = c;
            return this;
        }

        public NutritionalFacts build(){
            return nutrionalFacts;
        }
    }
}
FabianSR
  • 1
  • 1
  • Please explain how this answer solves the OP's original question. AFAICT, the original question was not about minimizing memory allocation. – William Price May 22 '22 at 21:08
  • It was just other solution; when the problem has been proposed for a class with multiple attributes... I suggested this solution that avoids having to declare the same attributes again (which could be many more than three) in the builder, part of the memory allocation involved is greater since the builder is pretty much the same object as the one you want to build... but really it not was the problem, sorry. – FabianSR May 23 '22 at 12:12
  • Yes, you're correct about some positive attributes of your solution -- but please make sure when you're answering a question that you're addressing the topic of the question. In this case, there was a compilation error. Suggestions on how to make a solution more efficient are better left as a comment once you earn just a bit more reputation. – William Price May 24 '22 at 16:27
  • I see, sorry for the inconveniences. I'll try to do it right next time. Thanks! – FabianSR May 25 '22 at 10:44