Is there any way to implement preliminary calculations before an invocation of super(...)
or this(...)
constructor? Consider the following example:
public class Test {
private final int n;
private final int m;
private final int[] store;
public Test(int n, int m) {
/* This is common (most generic) constructor of the class Test.
It is desirable to invoke it via this(...) call
from any other constructor of this class
since it contains some common initialization tasks,
which are better to concentrate in one place instead
of scattering them throught the class, due to
maintainability and security reasons. */
this.n = n;
this.m = m;
store = new int[n];
// ...
}
public Test(Object data) {
/* This is specific constructor. It depends on some parameters
which must be processed prior to a call of the Test(int n, int m)
constructor to get its arguments. The problem is, this processing
cannot appear here because a call to this(...) must be the first
statement in a constructor. */
int a; // Must be calculated via lengthy code
int b; // Must be calculated via lengthy code
this(a, b); // <- Compiler error here
// ... further initialization
}
}
How can I implement the parameters calculation? The simplest solution is to replace the specific constructor by a static method. But what if it must be a constructor and nothing else (e.g. there is a chance that it may be used in a descendant class). The best solution I've found so far is to introduce a static inner class containing all parameters of the common constructor and use it to store the parameters at the beginning of the specific constructor:
public class Test {
private final int n;
private final int m;
private final int[] store;
protected static class ConstrParams {
int nParam;
int mParam;
ConstrParams(int n, int m) {
nParam = n;
mParam = m;
}
}
protected Test(ConstrParams params) {
/* This is the common constructor now.
It is marked as protected because it is essentially auxiliary,
as well as the class ConstrParams is. */
n = params.nParam;
m = params.mParam;
store = new int[n];
// ...
}
public Test(int n, int m) {
// This is public interface to the common constructor.
this(new ConstrParams(n, m));
}
private static ConstrParams makeParams(Object data) {
/* This is a procedure that inserts lengthy calculations
before constructor chain invocation. */
int a = 0; // Calculate a from data
int b = 0; // Calculate b from data
return new ConstrParams(a, b);
}
public Test(Object data) {
// Specific constructor. Now compiles successfully.
this(makeParams(data));
// ... further initialization
}
}
Is there any better workaround? The case when Test(Object data)
must call some super(...)
constructor instead of this(...)
is even worse since we get less flexibility in this case and often cannot change the ancestor class's code.
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.