Шаблон Builder в эффективной Java

недавно я начал читать эффективную Java Джошуа Блоха. Я нашел идею шаблона Builder [пункт 2 в книге] действительно интересной. Я попытался реализовать его в своем проекте, но были ошибки компиляции. В сущности, я пытался сделать следующее:--3-->

класс с несколькими атрибутами и его класс builder:

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;
    }
}

класс, где я пытаюсь использовать класс выше:

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

Я получаю следующий компилятор ошибка:

заключительный экземпляр, содержащий effectivejava.BuilderPattern.NutritionalFacts.Строитель требуемый NutritionalFacts N = новый NutritionalFacts.Строитель(10).Карбо(23).жира(1).build();

Я не понимаю, что значит это сообщение. Пожалуйста объяснить. Приведенный выше код аналогичен примеру, предложенному Блохом в его книге.

8 ответов


сделать сайт static класса. Тогда это сработает. Если он нестатичен, ему потребуется экземпляр класса - владельца-и дело не в том, чтобы иметь экземпляр этого класса, и даже запретить создание экземпляров без построителя.

public class NutritionFacts {
    public static class Builder {
    }
}

ссылки: вложенные классы


вы должны сделать класс Builder статическим, а также вы должны сделать поля окончательными и иметь геттеры для получения этих значений. Не предоставляйте сеттеры для этих значений. Таким образом, ваш класс будет совершенно неизменным.

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;
    }
}

и теперь вы можете установить свойства следующим образом:

NutritionalFacts n = new NutritionalFacts.Builder().sodium(10).carbo(15).
fat(5).build();

чтобы создать внутренний конструктор в Intellij IDEA, проверьте этот плагин:https://github.com/analytically/innerbuilder


вы пытаетесь получить доступ к нестатическому классу статическим способом. Изменить Builder to static class Builder и это должно работать.

пример использования вы даете не потому, что нет экземпляра Builder присутствует. Статический класс для всех практических целей всегда создается. Если вы не сделаете его статическим, вам нужно будет сказать:

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

потому что вам нужно будет построить новый Builder каждый раз.


вам нужно объявить Builder внутренний класс as static.

посмотреть документацию для нестатические внутренние классы и статические внутренние классы.

в основном нестатических внутренних классов экземпляры не могут существовать без экземпляра внешнего класса.


класс Builder должен быть статическим. У меня сейчас нет времени тестировать код, но если он не сработает, дайте мне знать, и я посмотрю еще раз.


это означает, что вы не можете создать тип enclose. Это означает, что сначала вы должны подтвердить экземпляр "родительского" класса, а затем из этого экземпляра вы можете создать вложенные экземпляры класса.

NutritionalFacts n = new NutritionalFacts()

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

Вложенные Классы


Я лично предпочитаю использовать другой подход, когда у вас есть 2 разных классов. Поэтому вам не нужен статический класс. Это в основном, чтобы не писать Class.Builder когда вам нужно создать новый экземпляр.

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);
    }
    // ....
}

Итак, вы можете использовать свой конструктор следующим образом:

Person person = new PersonBuilder("attr1")
                            .setAttr2("attr2")
                            .build();