How to “insert” code before this(…) or super(…)?

瘦欲@ 提交于 2019-12-02 11:55:27

You could also create helper methods to compute a and b and call those in the this(...) expression. e.g.:

public Test(Object data) {
    this(computeA(data), computeB(data));
}

private static int computeA(Object data) {
    ...
}

private static int computeB(Object data) {
    ...
}

This solution is applicable for version of Java 8 and higher. I would create another constructor accepting Supplier<Integer>. The method Supplier::get() returns the value:

public Test(Supplier<Integer> n, Supplier<Integer> m) {
    this.n = n.get();
    this.m = m.get();
    store = new int[n.get()];
}

Which might be used in this way:

public Test(Object data) {
    this(() -> {
        int a = data.hashCode();  // expensive calculation
        return a;
    }, () -> {
        int b =  data.hashCode(); // expensive calculation
        return b;
    });
}

This approach would simplify another constructor and leaves only one primary constructor responsible for the encapsulation:

public Test(int n, int m) {
    this(() -> n, () -> m);
}

Here's a universal approach that I've found. It allows to inject any code before a call to this(...) or super(...) and thus to overcome Java's limitation of this(...) or super(...) to be the first statement in a constructor.

public class Test {
    private final int n;
    private final int m;
    private final int[] store;

    public Test(int n, int m) {
        // Primary constructor is unchanged
        this.n = n;
        this.m = m;
        store = new int[n];
        // ...
    }

    private static class ConstrParams {
        private int nParam;
        private int mParam;
        /* This class can also be used by more than one constructor
         or independently, to calculate the parameters and store
         them for other purposes. */
        private ConstrParams(Object data) {
            /* Calculate the parameters and/or do any other operations
             (preprocessing) that you would do in the specific constructor prior
             to calling another constructor. You may even add as many auxiliary
             methods as needed into this class and use them in this constructor. */
            nParam = 1;
            mParam = 2;
        }
    }

    /* Intermediate constructor, the main purpose of which is to extract
     parameters (if any) from a ConstrParams object and pass them to a primary
     or an inherited constructor. If ConstrParams produces no parameters but
     makes some pre-this() or -super() actions, this constructor makes
     insertion of such actions available. */
    private Test(ConstrParams params) {
        this(params.nParam, params.mParam);
        /* You can also call super(...) instead of this(...).
         When calling super(...), primary constructor may even not exist. */
//        super(params.nParam, params.mParam);
        /* As the reference to ConstrParams vanishes upon return to the
         calling constructor, you may want to make some actions connected
         with the params object (post-processing) or store the reference to it
         into this object. If so, here's the right place to do it. Otherwise,
         no further action is generally needed in this constructor. */
    }

    public Test(Object data) {
        // Specific constructor. Now compiles successfully.
        this(new ConstrParams(data));
        // ... further initialization
    }
}

Advantages include:

  • The code of constructor being called is unaffected in any way. This is especially useful when using super(...) because the changes to the ancestor class are often undesirable or impossible. When using this(...), the above approach doesn't affect any code relying on primary constructor.
  • It does not depend on the number of parameters the called constructor needs. Just add as many of them as you want as fields of the ConstrParams class and then extract prior to calling primary or inherited constructor. If parameters are computed jointly (i.e. it is impossible or expensive to split their calculations into two or more independent methods), this approach allows to do this. There may be (and often are) cases when the called constructor doesn't take any parameters and you just need to do some action in the dependent constructor prior to this(...) or super(...) call (one example of such action is logging). This solution allows you to make such action.
  • The auxiliary ConstrParams class that produces parameters and/or makes side effects can be used for additional purposes. You can introduce more constructors in it if more than one secondary constructor of the main class needs to overcome the this(...)/super(...) call limitation.
  • Applies uniformly to this(...) and super(...) calls.
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!