简体   繁体   中英

java Constructor overload with this(), how to execute code before this()

Today I met an interesting situation in a huge project. A class has several constructors which calls each other with this(), and at the end will call the init(), build() and so on. I wanted to set a flag and after that call the this() and the whole cumbersome process, but calling this() it should be the first.

How can I modify a code inside this class, without modifying the Contructor headers and set the flag? :)

I know it sounds like hackish, and maybe this aren't learned at schools, that's why is interesting at least for me. For others can be usefull too in some cases.

Here is a basic example, I did a few modification to simulate the real problem, the init() method. 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
    }
}

I got 1 easy implementation, but until I wrote this question I got the second one too.

My important questions are unanswered, but I hope this will be and I can accept somebody's answer, who want to build up reputation.

The init() method can't be coded twice or the logic of the code written in 2 places, because it is not a good programming paradigm, and that invokes maybe a few millions line of the code.

------------- Edit added ----------------

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");

        }

----------------------- Edit adding solution 1 -a very simple case

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

the reason why in a huge projet can't changed the function signatures are ( one of them) the dynamic loading, and reflection usage: 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-reflection-invoking-constructor-parameters.html Some IDE are enough intelligent to find refferences to this class even with Class.forName("java.awt.Rectangle"), if you have the sources, but if they are in third party libraryes ( plugins), than probably not. A licence checking routine want to hide himself and the developper is a bit experienced will split the "Rectangle" as "Rect" + "tagle" , or even more complex (decodeString) ( but this is enough. Highly doubt your super intelligent editor can find references than :)

The next solution can be with reflection ( that was the second one solution which I got typing here and I wrote above) -nobody mentioned yet

In a word: you can't. Java prohibits calling anything before this , so put that idea away.

that invokes maybe a few millions line of the code.

That's a problem in and of itself. I'd be very concerned if I were you.

This sounds less like constructors and more like a Builder pattern to me. Perhaps you should think about an alternative to constructors.

You need a constructor that sets the flag, even if it's private.

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

I'd think about refactoring the rest as well. Millions of lines of code? Oh, my.

Today I met an interesting situation in a huge project. A class has several constructors which calls each other with this(), and at the end will call the init(), build() and so on. I wanted to set a flag and after that call the this() and the whole cumbersome process, but calling this() it should be the first.

You can't. It sounds to me like the flag ought to be one of the arguments you pass to a chained (possibly private) constructor:

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

You can call code before the chained constructor is called, but:

  • It basically has to be a method invocation used to create one of the arguments for the chained constructor
  • It can't use this

Well the call to this() must be first, but one "hackish" way to get around this is to use factory design pattern and use static methodes it should look something like this:

public static Rectangle createRectangle(int width, int height) {
   //do some some stuff
   return new Rectangle(int x, int y, width, height)
}

I would modify the structure so that each of the constructors called the init() method with the appropriate parameters. Then, in the init() method, you can do what is currently in your 4-arg constructor. This allows the constructors to set flags and such while the actual initialization work happens later and can be made to respect the flags.

In this case, I'd go for a private constructor with an extra argument.

[...]
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();
}
[...]

As a general rule, it's generally good to avoid doing to much with constructors.

As the very documentation to which you link says:

If present, the invocation of another constructor must be the first line in the constructor.

pass the flag into the constructor as parameter, store it. (if it's used to determine which constructor was used to create the instance.) i don't see why you don't just call your mysterious code first thing in the master constructor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM