In a constructor in Java, if you want to call another constructor (or a super constructor), it has to be the first line in the constructor. I assume this is because you shouldn't be allowed to modify any instance variables before the other constructor runs. But why can't you have statements before the constructor delegation, in order to compute the complex value to the other function? I can't think of any good reason, and I have hit some real cases where I have written some ugly code to get around this limitation.
So I'm just wondering:
For an example of what I'm talking about, consider some code I wrote which I gave in this StackOverflow answer. In that code, I have a BigFraction class, which has a BigInteger numerator and a BigInteger denominator. The "canonical" constructor is the BigFraction(BigInteger numerator, BigInteger denominator)
form. For all the other constructors, I just convert the input parameters to BigIntegers, and call the "canonical" constructor, because I don't want to duplicate all the work.
In some cases this is easy; for example, the constructor that takes two long
s is trivial:
public BigFraction(long numerator, long denominator) { this(BigInteger.valueOf(numerator), BigInteger.valueOf(denominator)); }
But in other cases, it is more difficult. Consider the constructor which takes a BigDecimal:
public BigFraction(BigDecimal d) { this(d.scale() < 0 ? d.unscaledValue().multiply(BigInteger.TEN.pow(-d.scale())) : d.unscaledValue(), d.scale() < 0 ? BigInteger.ONE : BigInteger.TEN.pow(d.scale())); }
I find this pretty ugly, but it helps me avoid duplicating code. The following is what I'd like to do, but it is illegal in Java:
public BigFraction(BigDecimal d) { BigInteger numerator = null; BigInteger denominator = null; if(d.scale() < 0) { numerator = d.unscaledValue().multiply(BigInteger.TEN.pow(-d.scale())); denominator = BigInteger.ONE; } else { numerator = d.unscaledValue(); denominator = BigInteger.TEN.pow(d.scale()); } this(numerator, denominator); }
Update
There have been good answers, but thus far, no answers have been provided that I'm completely satisfied with, but I don't care enough to start a bounty, so I'm answering my own question (mainly to get rid of that annoying "have you considered marking an accepted answer" message).
Workarounds that have been suggested are:
BigInteger[]
or some kind of private inner class.The main argument against this functionality is that the compiler would have to check that you didn't use any instance variables or methods before calling the superconstructor, because the object would be in an invalid state. I agree, but I think this would be an easier check than the one which makes sure all final instance variables are always initialized in every constructor, no matter what path through the code is taken. The other argument is that you simply can't execute code beforehand, but this is clearly false because the code to compute the parameters to the superconstructor is getting executed somewhere, so it must be allowed at a bytecode level.
Now, what I'd like to see, is some good reason why the compiler couldn't let me take this code:
public MyClass(String s) { this(Integer.parseInt(s)); } public MyClass(int i) { this.i = i; }
And rewrite it like this (the bytecode would be basically identical, I'd think):
public MyClass(String s) { int tmp = Integer.parseInt(s); this(tmp); } public MyClass(int i) { this.i = i; }
The only real difference I see between those two examples is that the "tmp
" variable's scope allows it to be accessed after calling this(tmp)
in the second example. So maybe a special syntax (similar to static{}
blocks for class initialization) would need to be introduced:
public MyClass(String s) { //"init{}" is a hypothetical syntax where there is no access to instance //variables/methods, and which must end with a call to another constructor //(using either "this(...)" or "super(...)") init { int tmp = Integer.parseInt(s); this(tmp); } } public MyClass(int i) { this.i = i; }
The compiler knows that when an object of a child class is created, the base class constructor is called first.
To put it simply, you use multiple constructors for convenience (1st example) or to allow completely different initialization methods or different source types (2nd example. Show activity on this post. A class can have multiple constructors, as long as their signature (the parameters they take) are not the same.
Answer: Order of execution of constructors in inheritance relationship is from base /parent class to derived / child class.
Yes, any number of constructors can be present in a class and they can be called by another constructor using this() [Please do not confuse this() constructor call with this keyword]. this() or this(args) should be the first line in the constructor. This is known as constructor overloading.
I think several of the answers here are wrong because they assume encapsulation is somehow broken when calling super() after invoking some code. The fact is that the super can actually break encapsulation itself, because Java allows overriding methods in the constructor.
Consider these classes:
class A { protected int i; public void print() { System.out.println("Hello"); } public A() { i = 13; print(); } } class B extends A { private String msg; public void print() { System.out.println(msg); } public B(String msg) { super(); this.msg = msg; } }
If you do
new B("Wubba lubba dub dub");
the message printed out is "null". That's because the constructor from A is accessing the uninitialized field from B. So frankly it seems that if someone wanted to do this:
class C extends A { public C() { System.out.println(i); // i not yet initialized super(); } }
Then that's just as much their problem as if they make class B above. In both cases the programmer has to know how the variables are accessed during construction. And given that you can call super()
or this()
with all kinds of expressions in the parameter list, it seems like an artificial restriction that you can't compute any expressions before calling the other constructor. Not to mention that the restriction applies to both super()
and this()
when presumably you know how to not break your own encapsulation when calling this()
.
My verdict: This feature is a bug in the compiler, perhaps originally motivated by a good reason, but in its current form it is an artifical limitation with no purpose.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With