Перегрузка Java-конструктора с помощью this (), как выполнить код перед этим () - PullRequest
0 голосов
/ 15 марта 2012

Сегодня я встретил интересную ситуацию в огромном проекте.Класс имеет несколько конструкторов, которые вызывают друг друга с помощью this (), и в конце будут вызывать init (), build () и так далее.Я хотел установить флаг и после этого вызывать this () и весь громоздкий процесс, но при вызове this () он должен быть первым.

Как я могу изменить код внутри этого класса, не изменяяЗаголовки Contructor и установить флаг?:)

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

Вот базовый пример, я сделал несколько модификаций, чтобы смоделировать реальную проблему, метод init ().http://docs.oracle.com/javase/tutorial/java/javaOO/thiskey.html

public class Rectangle {
    private int x, y;
    private int width, height;
    private boolean flag;

    public Rectangle() {
      // execute code here, before this(), how?  -set the flag true for eg.
      this(0, 0, 0, 0);
    }
    public Rectangle(int width, int height) {
       // execute code here to, something different as above, before this(), how?   
       this(0, 0, width, height);
    }
    public Rectangle(int x, int y, int width, int height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        init();
    }
    private void init(){
      if(flag){
      ... do something new, else or different as the original, maybe return, even exit too
      }

      ... do something... the old code
    }
}

Я получил 1 простую реализацию, но пока я не написал этот вопрос, я получил и второй.

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

Метод init () нельзя кодировать дважды или логику кода, написанного в 2 местах, потому что это не хорошая парадигма программирования,и это вызывает, возможно, несколько миллионов строк кода.

------------- Редактирование добавлено ----------------

There is a way to known from which constructor was called: the full parametrized one or anything else with this() -I hope it gives more idea:

    public Rectangle(int x, int y, int width, int height) {
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;

            try {
                throw new RuntimeException("Hacking use a lot of imagination");
            } catch (Exception ex) {
                StackTraceElement[] stackTraces = ex.getStackTrace();
                // the first element is just above, no reason to check
                String thisClassName = getClass().getName();

                if (stackTraces[1].getClassName().equals(thisClassName)) {
                    if (stackTraces[1].getMethodName().equals("<init>")) {
                        flag = true;
                    }
                }
            }

            init();
        }

        private void init() {
            if (flag) {
                System.out.println("\"... do something new, else or different as the original, maybe return, even exit too\"");
            }

            System.out.println("\"... do something... the old code");

        }

----------------------- Редактировать добавление решения 1 - очень простой случай

    public Rectangle() {
        // execute code here, before this(), how?  -set the flag true for eg.
        this(doVeryBanalHack(0, false), 0, 0, 0);
    }

    public Rectangle(int width, int height) {
        // execute code here to, something different as above, before this(), how?   
        this(doVeryBanalHack(0, false), 0, width, height);
    }

    public Rectangle(int x, int y, int width, int height) {
        this.x = doVeryBanalHack(x, true);
        this.y = y;
        this.width = width;
        this.height = height;
             // TODO deal with concurrency if you are in multithreaded environment, otherwise is done
           this.flag = nextValueOfFlag;
           init();
....}

   private static boolean nextValueOfFlag;

   private static int doVeryBanalHack(int retValue, boolean flagValue) {
           System.out.println("\"execute code here, before this() it is too simple, it is banal static function\");
         // TODO deal with concurrency if you are in multithreaded environment
        nextValueOfFlag = flagValue;
   }

Причина, по которой в огромном проекте невозможно изменить, подписи функций (одна из них) - динамическая загрузка и использование отражения: http://tutorials.jenkov.com/java-reflection/constructors.html http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html

http://www.java -forums.org /java-lang / 7896-object-отражение-invoking-constructor-parameters.html Некоторые IDE достаточно интеллектуальны, чтобы найти ссылки на этот класс даже с Class.forName ("java.awt.Rectangle"), если у вас есть tИсточники, но если они находятся в сторонних библиотеках (плагинах), то, вероятно, нет.Подпрограмма проверки лицензии хочет скрыть себя, и разработчик, немного опытный, разделит «Rectangle» на «Rect» + «tagle» или даже более сложный (decodeString) (но этого достаточно. Очень сомневаюсь, что ваш суперинтеллектуальный редактор можетнайти ссылки, чем:)

Следующее решение может быть с отражением (это было второе решение, которое я набрал здесь и написал выше) - пока никто не упомянул

Ответы [ 7 ]

5 голосов
/ 15 марта 2012

Одним словом: вы не можете. Java запрещает вызывать что-либо до this, так что убери эту идею.

, который вызывает, возможно, несколько миллионов строк кода.

Это проблема сама по себе. Я был бы очень обеспокоен на вашем месте.

Для меня это звучит не так, как конструкторы, а скорее как паттерн Строитель. Возможно, вам следует подумать об альтернативе конструкторам.

Вам нужен конструктор, который устанавливает флаг, даже если он закрытый.

public class Rectangle {
    private int x, y;
    private int width, height;
    private boolean flag;

    private Rectangle(int x, int y, int w, int h, boolean doInit) {
        this.x = y;
        this.y = y;
        this.width = w;
        this.height = h;
        this.flag = doInit;
        // Do what you must after this; adjust other ctors accordingly.
    }
}

Я бы тоже подумал о рефакторинге остальных. Миллионы строк кода? О, мой.

2 голосов
/ 15 марта 2012

Что ж, вызов this() должен быть первым, но один "хакерский" способ обойти это - использовать шаблон фабричного дизайна и использовать статические методы, он должен выглядеть примерно так:

public static Rectangle createRectangle(int width, int height) {
   //do some some stuff
   return new Rectangle(int x, int y, width, height)
}
2 голосов
/ 15 марта 2012

Сегодня я встретил интересную ситуацию в огромном проекте. Класс имеет несколько конструкторов, которые вызывают друг друга с помощью this (), и в конце будут вызывать init (), build () и так далее. Я хотел установить флаг и после этого вызывать this () и весь громоздкий процесс, но при вызове this () он должен быть первым.

Ты не можешь. Мне кажется, что флаг должен быть одним из аргументов, передаваемых цепочечному (возможно, частному) конструктору:

public Rectangle() {
  this(false, 0, 0, 0, 0);
}

public Rectangle(int width, int height) {
   this(true, 0, 0, width, height);
}

private Rectangle(boolean flag, int x, int y, int width, int height) {
    // Use everything including flag.
}

Вы можете вызвать код до вызова связанного конструктора, но:

  • В основном это должен быть вызов метода, используемый для создания одного из аргументов для связанного конструктора
  • Не может использовать this
1 голос
/ 15 марта 2012

В этом случае я бы выбрал приватный конструктор с дополнительным аргументом.

[...]
public Rectangle() {
    this(true, 0, 0, 0, 0);
}
public Rectangle(int width, int height) {  
    this(false, 0, 0, width, height);
}
public Rectangle(int x, int y, int width, int height) {
    this(true, x, y, width, height);
}
private Rectangle(boolean flag, int x, int y, int width, int height) {
    this.flag = flag;
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    init();
}
[...]

Как правило, лучше избегать многого с конструкторами.

1 голос
/ 15 марта 2012

Я бы изменил структуру так, чтобы каждый из конструкторов вызвал метод init() с соответствующими параметрами.Затем в методе init() вы можете делать то, что в данный момент находится в вашем конструкторе с 4 аргументами.Это позволяет конструкторам устанавливать флаги и тому подобное, в то время как фактическая работа по инициализации происходит позже и может быть выполнена с учетом флагов.

0 голосов
/ 15 марта 2012

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

0 голосов
/ 15 марта 2012

Как указано в самой документации, на которую вы ссылаетесь:

Если присутствует, вызов другого конструктора должен быть первой строкой в ​​конструкторе.

...