Как написать переопределяющий метод, который возвращает экземпляр вызывающего класса в Java

Я относительно новичок в шаблонах Java и дизайна. Я пытаюсь реализовать шаблон Builder для своего приложения. У меня есть interface, у которого есть метод build, этот метод build примет класс в качестве параметра и вернет то же самое.

public interface TestInterface {
    public TestInterface withTest(int start);
    public <T> T build();
}

На данный момент я реализовал этот interface в одном классе и переопределил методы в классе GenerateBuilder, и он отлично работает.

public class GenerateNumbers {

private String start;

private GenerateGSRN(GenerateBuilder builder) {
        this.start = builder.start;
    }

    public static class GenerateBuilder implements TestInterface {

        private String start;

        @Override
        public TestInterface withGcp(String start) {
            this.start = start;
            return this;
        }

        @Override
        public GenerateNumbers build() {
            return new GenerateNumbers(this);
        }

    }
}

Но я хочу переместить класс GenerateBuilder, который переопределяет методы, в свой собственный отдельный класс, чтобы его мог использовать любой другой класс (сделайте его общим, чтобы мне не пришлось писать этот код снова).

Но, как мы видим, функция GenerateBuilder Build тесно связана с GenerateNumbers, из-за чего я не могу ее переместить. Я хочу изменить метод Build в Interface, а также во время overriding, чтобы он возвращал экземпляр класса вызывающему классу.

Например: если GenerateNumbers вызывает метод сборки, то метод build должен возвращать GenerateNumbers. Если вызывается GenerateNumbersRandom, то метод build должен возвращать экземпляр GenerateNumbersRandom.

Я пробовал пару вещей, но не работал:

В интерфейсе: public <T> T build(Class clazz);

В переопределении:

@Override
        public <T> T build(Class clazz) {
            return clazz.newInstance();
        }

Надеюсь, я смог правильно объяснить проблему. Может кто-нибудь предложить мне, как сделать эту работу.


person BATMAN_2008    schedule 06.01.2021    source источник
comment
Просто чтобы убедиться, что я понимаю, вы хотите, чтобы экземпляр дочернего класса создавал другой экземпляр того же класса, вызывая родительский метод Eventually final?   -  person Vincent C.    schedule 06.01.2021
comment
Да, я хочу, чтобы метод build возвращал экземпляр дочернего класса, который вызывает его. Например, в этом случае, когда GenerateNumbers делает вызов, он должен создать GenerateNumbers , аналогичный тому, как я это сделал сейчас в жестко закодированном значении.   -  person BATMAN_2008    schedule 06.01.2021
comment
Мой главный вопрос будет: почему? Вам нужен универсальный класс, который может создавать универсальный объект-конструктор, который, в свою очередь, может генерировать другой универсальный объект. Как вы думаете, зачем вам нужна вся эта сложность? Какую основную проблему вы пытаетесь решить?   -  person fishinear    schedule 06.01.2021
comment
У меня есть несколько классов, похожих на GenerateNumbers, но выдающих разные результаты. Если я не создам универсальный класс, то в конечном итоге я напишу один и тот же код в каждом классе, но с другим именем класса. Следовательно, я хотел бы создать универсальный класс, чтобы я мог использовать его во всех классах и не должен создавать этот класс во всех классах, которые я создаю.   -  person BATMAN_2008    schedule 06.01.2021


Ответы (1)


Насколько я понимаю, вы могли бы:

  • объявите, что ваш интерфейс имеет общий тип (Builder)
  • объявите тип, который вы хотите построить с помощью класса, реализующего интерфейс (NumberGenerator)
  • объявить построитель как реализацию интерфейса, имеющего для универсального типа класс, который он будет строить (NumberGeneratorBuilder implements Builder<NumberGenerator>)
  • в интерфейсе Builder доступ к фактическому типу универсального объекта во время выполнения для создания экземпляра нового экземпляра этого типа.

Например, это даст что-то вроде:

import java.lang.reflect.ParameterizedType;

public interface Builder<T> {

  default T build() throws IllegalAccessException, InstantiationException {
    // in a more production-ready application, you would not reference item with their index but lookup through correct criterion to avoid getting a bad class instantiated
    return ((Class<T>) ((ParameterizedType) this.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0]).newInstance();
  }
}
public class NumberGenerator {

  public static NumberGenerator instance() throws InstantiationException, IllegalAccessException {
    return new NumberGeneratorBuilder().build();
  }

  // Note that visibility is important here, default constructor needs to be visible from the Builder class, and not from its implementation
  NumberGenerator() {

  }

  public static class NumberGeneratorBuilder implements Builder<NumberGenerator> {

  }

}

person Vincent C.    schedule 06.01.2021
comment
Большое спасибо, что нашли время и ответили. Но на самом деле я ищу ответ, где мне нужно просто изменить свой метод Build в Interface и в реализации. Я не хочу вносить изменения во весь интерфейс, так как у него есть много других методов, которые возвращают разные формы данных. Я просто хочу знать, как метод Build может принимать входные данные как класс и возвращать экземпляр того же класса вызывающему классу. - person BATMAN_2008; 06.01.2021
comment
Итак, вы не можете добавить общий тип к вашему TestInterface? - person Vincent C.; 06.01.2021
comment
На самом деле, я бы не хотел вносить какие-либо изменения в сам Interface, так как у него много методов и он реализован многими классами. Я хотел бы просто внести изменения в мой метод build внутри него, а также в метод build, который был переопределен в реализованных классах. - person BATMAN_2008; 07.01.2021