Абстрактный строитель, создающий неизменяемый объект

У меня есть следующий код, реализующий абстрактный построитель (как в эффективной книге Java):

interface I {
    I ret();
}

abstract class A implements I {

    private String s = "";

    A(Builder b) {
        s = b.s;
    }

    @Override
    public I ret() {
        String s = "some new string from calc.";
        //HERE I NEED TO CONSTRUCT class B
        //THIS IS PROBLEMATIC LINE BELOW <<<<--------------------------------------
        return new Builder<>().withString(s).build();
    }

    static abstract class Builder<T extends Builder<T>> {

        String s = "";

        T withString(String s) {
            this.s = s;
            return self();
        }

        protected abstract A build();

        //simulated self-type idiom
        protected abstract T self();
    }
}

class B extends A {

    private B(A.Builder b) {
        super(b);
    }

    static class Builder extends A.Builder<Builder> {

        @Override
        protected B build() {
            return new B(this);
        }

        @Override
        protected Builder self() {
            return this;
        }
    }
}

и это небольшой вариант использования

public static void main(String[] args) {
    I b = new B.Builder().withString("bclass").build();
    I b2 = b.ret(); //<---- this one should construct new immutable B
}

Я хочу сделать следующее: из метода ret() в классе A я хотел бы создать неизменяемый объект, не знающий родительского типа, поэтому в моем примере это будет новый класс B с новой внутренней строкой.

Проблема в том, что класс A не знает о классе B, поскольку B является его родительским.

Возможно ли что-то подобное, не прибегая к дженерикам?

Если бы я использовал изменяемый класс, это было бы так же просто, как this.s = newS;.

ОБНОВИТЬ

abstract class A implements I {

    private String s = "";

    A(Builder b) {
        s = b.s;
    }

    @Override
    public I ret() {
        String s = "SOME NEW STING FROM CALCULATION";
        return builder().withString(s).build();
    }

    abstract Builder<?> builder();

    static abstract class Builder<T extends Builder<T>> {

        String s = "";

        T withString(String s) {
            this.s = s;
            return self();
        }

        protected abstract A build();

        //simulated self-type idiom
        protected abstract T self();
    }
}

class B extends A {

    B(A.Builder b) {
        super(b);
    }

    @Override
    Builder builder() {
        return new Builder();
    }

    static class Builder extends A.Builder<Builder> {

        @Override
        protected B build() {
            return new B(this);
        }

        @Override
        protected Builder self() {
            return this;
        }
    }
}

Что сюда добавляет строитель? Разве вы не можете просто вызвать (частный?) Конструктор напрямую?

Andy Turner 17.12.2018 13:22

Я удалил много кода, чтобы он стал чище. Просто сконцентрировался на своей проблеме.

Bojan Vukasovic 17.12.2018 13:23
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
2
248
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам необходимо предоставить абстрактный метод в A для создания подходящего экземпляра Builder:

abstract Builder<A> toBuilder();

Затем реализуйте это в конкретных подклассах и вызовите:

return toBuilder().withString(s).build();

спасибо, выглядит хорошо. Надеюсь, что эта строчка abstract Builder<?> builder(); такая как надо делать? Я обновил пример новым кодом.

Bojan Vukasovic 17.12.2018 14:24

Другие вопросы по теме