Java Forum / First Aid / January 2006
throwing exception from constructor
Jeff Higgins - 14 Jan 2006 16:36 GMT Hi, Is it poor practice to throw an exception from a constructor?
What is the status of an object whose constructor throws an exception: constructed, partially constructed, not constructed?
Would this be considered bad practice?
public class TestClass { public TestClass(long arg1, long arg2) throws IllegalArgumentException { if (arg1 > 0xffffffffL || arg2 > 0xffffffffL) { throw new IllegalArgumentException(); } else { this.arg1 = arg1; this.arg2 = arg2; } } public long arg1; public long arg2; }
Thanks. Jeff Higgins
Thomas Hawtin - 14 Jan 2006 17:15 GMT > Is it poor practice to throw an > exception from a constructor? No.
> What is the status of an object > whose constructor throws an exception: > constructed, partially constructed, not constructed? From a language point of view, it makes little difference. So long as super has been called (possibly implicitly) and the Object constructor has returned normally, then the finalize method will be run appropriately.
If for some reason you leak a reference to this, then any final fields will not have been assigned.
You may be thinking of C++ where exceptions from constructors interact with other language features.
> Would this be considered bad practice? > [quoted text clipped - 5 lines] > } > else { There is no need for the else. (You might also want to check if the arguments are negative.)
> this.arg1 = arg1; > this.arg2 = arg2; > } > } > public long arg1; > public long arg2; Public variables are very bad practice. Also, if you don't intend to change member variables, make them final.
> } Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Jeff Higgins - 14 Jan 2006 18:39 GMT >> Is it poor practice to throw an >> exception from a constructor? [quoted text clipped - 8 lines] > super has been called (possibly implicitly) and the Object constructor has > returned normally, then the finalize method will be run appropriately. I certainly have a lot more to read but in the meantime, I'm hoping what you mean here is that there is basically there is nothing that I can do with this "object" any way.
> If for some reason you leak a reference to this, then any final fields > will not have been assigned. I think this is what I might have been concerned about. How might I accomplish leaking a reference to this?
> You may be thinking of C++ where exceptions from constructors interact > with other language features. [quoted text clipped - 10 lines] > > There is no need for the else.
> (You might also want to check if the arguments are negative.) Yes. Thank you very much, I overlooked that
>> this.arg1 = arg1; >> this.arg2 = arg2; >> } >> } >> public long arg1; >> public long arg2; When I do: private final long arg1; private final long arg2; and eliminate the else statement, I receive back an error saying the blank fields arg1 & arg2 may not have been initialized.
If I try: private final long arg1 = null; private final long arg2 = null; I receive back an error cannot convert from null to long.
How to accomplish without the else statement?
> Public variables are very bad practice. Yes.
> Also, if you don't intend to change member variables, make them final. Yes.
>> } > > Tom Hawtin Thank you very much for your time & help Jeff Higgins
Andrew McDonagh - 14 Jan 2006 18:48 GMT sniped
> When I do: > private final long arg1; [quoted text clipped - 10 lines] > > How to accomplish without the else statement? primitives (int long, boolean, etc) can not be assigned the NULL value - hence the error you get. They need to be initialised with a valid value for their primitive type. So in your case this can be '0' (zero)
int a = 0; (or -1, 2345678, etc..) long b = 0; (or -1, 2345678, etc..) boolean c = true; (or false) etc
Only references to Objects can be assign null.
now there are Object wrappers available for each of the primitives and so any references of these types can be assigned null.
e.g.
Integer a = null; Long b = null; Boolean c = null;
(Note the capitalization of the first letter).
Jeff Higgins - 14 Jan 2006 19:22 GMT [snip]
> primitives (int long, boolean, etc) can not be assigned the NULL value - > hence the error you get. ... Thank you Andrew.
Are the assignments of arg1 & arg2 to null unnecessary here?
public class TestClass {
public TestClass(long arg1, long arg2) throws IllegalArgumentException { if (arg1 > 0xffffffffL || arg1 < 0 || arg2 > 0xffffffffL || arg2 < 0) { this.arg1 = null; this.arg2 = null; throw new IllegalArgumentException(); } else { this.arg1 = arg1; this.arg2 = arg2; } } public Long getArg1() { return arg1; } public Long getArg2() { return arg2; } private final Long arg1; private final Long arg2; }
Andrew McDonagh - 14 Jan 2006 19:27 GMT > [snip] > [quoted text clipped - 29 lines] > private final Long arg2; > } yes, any object references of a class are automatically assigned to null if you dont explicitly assign them yourself.
note: your private final Long arg1 & 2 are references to Long objects, yet your constructor parameters are long primitives - you will get a compiler error if you try and assign the primitives to the references.
if you want to do this then you will need to do the following in the constructor...
this.arg1 = new Long(arg1); this.arg2 = new Long(arg2);
or change the private variables to be primitives...
Andrew
Thomas Hawtin - 14 Jan 2006 19:53 GMT >> Are the assignments of arg1 & arg2 to null unnecessary here?
>> public TestClass(long arg1, long arg2) >> throws IllegalArgumentException { You don't need to declare runtime exceptions, although it should appear in the JavaDoc.
>> if (arg1 > 0xffffffffL || arg1 < 0 || arg2 > 0xffffffffL || >> arg2 < 0) { >> this.arg1 = null; >> this.arg2 = null; >> throw new IllegalArgumentException();
> yes, any object references of a class are automatically assigned to null > if you dont explicitly assign them yourself. final member variables will need an assignment (although you can see their initial null value with sufficient hackery).
If the constructor leaves abnormally, then final member variable need not be assigned. So, the compiler is write, the null assignments are unnecessary. So the exciting chapter in the JLS on definite assignment.
> note: your private final Long arg1 & 2 are references to Long objects, > yet your constructor parameters are long primitives - you will get a > compiler error if you try and assign the primitives to the references. 1.5 will autobox.
> this.arg1 = new Long(arg1); 1.5 adds Long.valueOf(long), which may or may not return a shared value, saving creation of lots of small objects. Sun's implementation will give shared values in the range [-128, 127], but other implementations may not (for Integer, that range will always give a shared value).
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Roedy Green - 14 Jan 2006 23:37 GMT On Sat, 14 Jan 2006 20:03:08 +0000, Thomas Hawtin <usenet@tackline.plus.com> wrote, quoted or indirectly quoted someone who said :
>You don't need to declare runtime exceptions, although it should appear >in the JavaDoc. Javadoc has gone through spelling evolution. It started out JavaDOC, then JavaDoc, and now Sun is consistently using Javadoc and javadoc.exe for the utility.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Chris Smith - 14 Jan 2006 19:53 GMT > note: your private final Long arg1 & 2 are references to Long objects, > yet your constructor parameters are long primitives - you will get a > compiler error if you try and assign the primitives to the references. My guess is Jeff is using Java 1.5, which will perform the boxing conversion automatically.
 Signature www.designacourse.com The Easiest Way To Train Anyone... Anywhere.
Chris Smith - Lead Software Developer/Technical Trainer MindIQ Corporation
Andrew McDonagh - 14 Jan 2006 19:54 GMT >>note: your private final Long arg1 & 2 are references to Long objects, >>yet your constructor parameters are long primitives - you will get a >>compiler error if you try and assign the primitives to the references. > > My guess is Jeff is using Java 1.5, which will perform the boxing > conversion automatically. true, if he is...
Chris Smith - 14 Jan 2006 19:31 GMT > Are the assignments of arg1 & arg2 to null unnecessary here? Yes. The value can not be observed, so there's no reason to assign it. No compliant virtual machine running a programming including that class will behave any differently if you don't set the values to null. (Besides the fact that they are null anyway, since that's the default value for a field.)
 Signature www.designacourse.com The Easiest Way To Train Anyone... Anywhere.
Chris Smith - Lead Software Developer/Technical Trainer MindIQ Corporation
Stefan Schulz - 14 Jan 2006 18:42 GMT > Public variables are very bad practice. Also, if you don't intend to > change member variables, make them final. <offtopic> While i agree in principle, i will not let it stand in its entirety. Primitive public finals do have a place, and some "container" or "parameter" type objects which have little function except for holding some values (GridBagConstraints, for example) might also legitimately have public fields.
That being said, think long and hard before putting that "public" keyword in front of a field. They do have a use and a place in the language - but 99% of the time, this class won't be one of those instances. </offtopic>
Thomas Hawtin - 14 Jan 2006 19:59 GMT > While i agree in principle, i will not let it stand in its entirety. > Primitive public finals do have a place, and some "container" or > "parameter" type objects which have little function except for holding > some values (GridBagConstraints, for example) might also legitimately > have public fields. I think GridBagConstraints is a good example of how not to design a class.
It is common for objects like this to allow chaining. Instead of witting:
cons.gridx = 1; cons.gridy = 2;
It's often considered better (although not by all), to be able to write:
cons .gridx(1) .gridy(2);
That also has the advantage that we can check that the values at legal at the point they are assigned. Special values can have their own methods, rather than dodgy constants.
Further, setting both gridx and gridy at the same time makes a sensible operation, so we can have:
cons.gridLocation(1, 2);
OTOH, Data Transfer Objects (DTOs, formerly known as Value Objects), are very dull to write.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Oliver Wong - 20 Jan 2006 21:05 GMT >> While i agree in principle, i will not let it stand in its entirety. >> Primitive public finals do have a place, and some "container" or [quoted text clipped - 28 lines] > > Tom Hawtin If you don't need to do any value-checking, you can have it both ways.
<code> class PairOfInts { public int x, y;
public PairOfInts setX(int newX) { this.x = newX; return this; }
public PairOfInts setY(int newY) { this.y = newY; return this; }
public PairOfInts setBoth(int newX, int newY) { this.x = newX; this.y = newY; return this; } } </code>
If you DO need value checking... well... don't make the fields public.
- Oliver
Thomas Hawtin - 21 Jan 2006 00:30 GMT > If you don't need to do any value-checking, you can have it both ways.
> class PairOfInts { > public int x, y; > > public PairOfInts setX(int newX) { Then you have added a non-standard way of doing things on top of a standard way. That is needlessly confusing the interface.
> If you DO need value checking... well... don't make the fields public. Just don't make non-final static fields public.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Jeff Higgins - 14 Jan 2006 23:04 GMT > Hi, > Is it poor practice to throw an [quoted text clipped - 23 lines] > Thanks. > Jeff Higgins Thanks to all for your helpful comments.
Acceptable to throw exceptions from constructor. Public variables bad (generally). Unchanging variables should be declared final. Primitive types cannot be assigned null. Reference types may be assigned null. Fields not explicitly initialized will be initilaized to a default value. Final fields need to be explicitly initialized. Autoboxing is available in 1.5. No need to declare runtime exceptions. 1.5 Long.valueOf(long) and shared values.
I guess the one remaining question I might have is the question (in my mind) raised by the comment by Thomas Hawtin.
"If for some reason you leak a reference to this, then any final fields will not have been assigned."
Much appreciated. Jeff Higgins
Jeff Higgins - 14 Jan 2006 23:10 GMT > Hi, > Is it poor practice to throw an [quoted text clipped - 23 lines] > Thanks. > Jeff Higgins Thanks to all for your helpful comments.
Acceptable to throw exceptions from constructor. Public variables bad (generally). Unchanging variables should be declared final. Primitive types cannot be assigned null. Reference types may be assigned null. Fields not explicitly initialized will be initilaized to a default value. Final fields need to be explicitly initialized. Autoboxing is available in 1.5. No need to declare runtime exceptions. 1.5 Long.valueOf(long) and shared values.
I guess the one remaining question I might have is the question (in my mind) raised by the comment by Thomas Hawtin.
"If for some reason you leak a reference to this, then any final fields will not have been assigned."
Much appreciated. Jeff Higgins
Thomas Hawtin - 15 Jan 2006 01:56 GMT > I guess the one remaining question I might have > is the question (in my mind) raised by the comment > by Thomas Hawtin. > > "If for some reason you leak a reference to this, > then any final fields will not have been assigned." You can do something like this contrived example:
class X { private final String name; X() { this.name = "Fred"; } X(java.util.List<Object> list) { list.add(this); throw new IllegalArgumentException(); } @Override public String toString() { return name.toString(); } public static void main(String[] args) { java.util.List<Object> list = new java.util.ArrayList<Object>(); try { new X(list); } finally { System.err.println(list); } } }
The second constructor will append the object to the list. You then get an exception, so the final member variable is not assigned, but the instance is still available through the list. If toString is called on the object, it NPEs as name is null.
So while you may expect the program to throw an IllegalArgumentException, it actually NPEs.
$ java X Exception in thread "main" java.lang.NullPointerException at X.toString(X.java:13) at java.lang.String.valueOf(String.java:2665) at java.lang.StringBuilder.append(StringBuilder.java:115) at java.util.AbstractCollection.toString(AbstractCollection.java:355) at java.lang.String.valueOf(String.java:2665) at java.io.PrintStream.println(PrintStream.java:771) at X.main(X.java:20)
So best avoid that sort of construction.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Jeff Higgins - 15 Jan 2006 03:58 GMT [snip}
> The second constructor will append the object to the list. You then get an > exception, so the final member variable is not assigned, but the instance [quoted text clipped - 5 lines] > > So best avoid that sort of construction. Thomas, Thanks for your generous help. I'm sorry if I'm beating a dead horse. But I think I may have a situation as you describe.
(Caution: code will not compile.)
public class TagDemo {
public static void main(String args) { try { inputStream = new FileInputStream(in); inputChannel = inputStream.getChannel(); } catch(Exception e) { e.printStackTrace(); } ArrayList<Tag> list = new ArrayList<Tag>(); ByteBuffer buf = ByteBuffer.allocate(4); inputChannel.read(buf); buf.flip(); int tag = buf.getChar(); long count = buf.getInt() & 0xffffffff; long value = buf.getInt() & 0xffffffff; list.add(new Tag(tag, value, count)); buf.clear(); } }
public class Tag {
public Tag(Integer tag, Long value, Long count) { initializeMap(); if (value > 0xffffffffL || value < 0 || count > 0xffffffffL || count < 0) { throw new IllegalArgumentException(); } else { if (isKnownTag(tag)) { Class _class = Class.forName(tagMap.get(tag)); Constructor _constr = _class.getConstructor( new Class[] {Integer.class, Long.class, Long.class}); meta = (TagMeta)_constr.newInstance(new Object[] {tag, value, count}); } else meta = new TagMeta(null, null); } } private final TagMeta meta; public static final HashMap<Integer, String> tagMap = new HashMap<Integer, String>(); public static final Boolean isKnownTag(Integer tag) { return tagMap.containsKey(tag); } public static final initializeMap() { tagMap.put(256, "ImageWidth); } }
public class TagMeta {
public TagMeta(Long value, Long count) {
} public Long getValue() { return value; } public Long getCount() { return count; } public Integer getTag() { return null; } public Integer getName() { return null; } public Integer getShortDescription() { return null; }
private final Long value; private final Long count; }
public class ImageWidth extends TagMeta {
public ImageWidth(Long value, Long count) { super(value, count); } @Override public Integer getTag() { return tag; } @Override public Integer getName() { return name; } @Override public Integer getShortDescription() { return shortDescription; }
private final Integer tag = 256; private final String name = "ImageWidth"; private final String shortDescription = "The number of columns in the image, " + "i.e. the number of pixels per row.";
}
Jeff Higgins - 15 Jan 2006 14:59 GMT Sorry for the hasty post. This does compile, and I guess that adding a Tag with a TagMeta with null values doesn't really matter. I can just test for that. In fact, it seems not to apply to the situation you described. Thanks. Jeff
package higgins.jeff.test;
import java.util.ArrayList;
public class TagDemo {
public static void main(String[] args) { ArrayList<Tag> list = new ArrayList<Tag>(); int tag = 257; long count = -0xffffffffL & 0xffffffffL; long value = 0xffffffffL & 0xffffffffL; try { list.add(new Tag(tag, value, count)); } catch(Exception e) { e.printStackTrace(); } if (list.get(0).getTag() == null) { System.out.println("Found unknown tag."); } else { System.out.println(list.get(0).getName() + ".tag = " + list.get(0).getTag()); System.out.println(list.get(0).getName() + ".value = " + list.get(0).getValue()); System.out.println(list.get(0).getName() + ".count = " + list.get(0).getCount()); System.out.println(list.get(0).getName() + ".shortDescription = " + list.get(0).getShortDescription()); } } }
package higgins.jeff.test;
import java.util.HashMap; import java.lang.reflect.Constructor;
public class Tag {
public Tag(Integer tag, Long value, Long count) throws Exception { initializeMap(); if (value > 0xffffffffL || value < 0 || count > 0xffffffffL || count < 0) { throw new IllegalArgumentException(); } else { if (isKnownTag(tag)) { Class _class = Class.forName("higgins.jeff.test." + tagMap.get(tag)); Constructor _constr = _class.getConstructor( new Class[] {Integer.class, Long.class, Long.class}); meta = (TagMeta)_constr.newInstance(new Object[] {tag, value, count}); } else meta = new TagMeta(null, null, null); } } public Integer getTag() { return meta.getTag(); } public Long getValue() { return meta.getValue(); } public Long getCount(){ return meta.getCount(); } public String getName() { return meta.getName(); } public String getShortDescription() { return meta.getShortDescription(); } private final TagMeta meta; public static final HashMap<Integer, String> tagMap = new HashMap<Integer, String>(); public static final Boolean isKnownTag(Integer tag) { return tagMap.containsKey(tag); } public static final void initializeMap() { tagMap.put(256, "ImageWidth"); } }
package higgins.jeff.test;
public class TagMeta { public TagMeta(Integer tag, Long value, Long count) { this.tag = tag; this.value = value; this.count = count; }
public Integer getTag() { return null; }
public Long getValue() { return value; }
public Long getCount() { return count; }
public String getName() { return null; }
public String getShortDescription() { return null; } private final Integer tag; private final Long value; private final Long count; }
package higgins.jeff.test;
public class ImageWidth extends TagMeta { public ImageWidth(Integer tag, Long value, Long count) { super(tag, value, count); }
@Override public Integer getTag() { return tag; }
@Override public String getName() { return name; }
@Override public String getShortDescription() { return shortDescription; }
private final Integer tag = 256;
private final String name = "ImageWidth";
private final String shortDescription = "The number of columns in the image, " + "i.e. the number of pixels per row."; }
Thomas Hawtin - 15 Jan 2006 17:55 GMT > public static void main(String[] args) { > ArrayList<Tag> list = new ArrayList<Tag>(); [quoted text clipped - 3 lines] > try { > list.add(new Tag(tag, value, count)); That bit is fine, so far.
> } > catch(Exception e) { e.printStackTrace(); } > if (list.get(0).getTag() == null) { If an Exception of some description is thrown (possibly a RuntimeException from practically anywhere), then your list will have no elements. Attempting to get element zero of an empty list is unfortunate. There is no way for list.get(0) to return null at that point.
Rather than repeatedly retrieving a value from a list (or similar), it's generally better to get the reference out of the list into a local variable, and then do what you will to it.
> public class Tag { > > public Tag(Integer tag, Long value, Long count) throws Exception { > initializeMap(); If you passed tag to the initializeMap static method and it stored that in the static map, then you would have altered state that will persist even if the constructor throws. If you passed 'this' in as well, you would have the problem of access to a partially initialised object.
> if (value > 0xffffffffL || value < 0 || count > 0xffffffffL || count > < 0) { [quoted text clipped - 8 lines] > meta = (TagMeta)_constr.newInstance(new Object[] {tag, > value, count}); IIRC, these methods are declared with ..., so you can drop the new Class[] { }/new Object[] { }.
I would tend to avoid reflection if at all possible.
> } > else meta = new TagMeta(null, null, null); I would tend to move this sort of stuff off into another method that calls this constructor. It's not a bad idea to keep constructors simple.
> } > }
> private final TagMeta meta; > public static final HashMap<Integer, String> tagMap = new [quoted text clipped - 5 lines] > tagMap.put(256, "ImageWidth"); > } It's conventional to put member variables at the top of classes.
Variable statics are very, very, bad. Public variables also bad. If you just need the Map interface, declare the variable as Map type.
If you want to initialize a map, or other structure, beyond simple assignment, then either use a static initialiser or use a static method to create set it up.
private static final Map<Integer, String> tagMap; static { tagMap = new java.util.HashMap<Integer, String>(); tagMap.put(256, Tags.IMAGE_WIDTH); } or private static final Map<Integer, String> tagMap = createTagMap(); private static Map<Integer, String> createTagMap() { Map<Integer, String> tagMap = new java.util.HashMap<Integer, String>(); tagMap.put(256, Tags.IMAGE_WIDTH); return tagMap; }
> public class TagMeta {
> public class ImageWidth extends TagMeta { Generally, non-leaf classes should be abstract.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Jeff Higgins - 15 Jan 2006 18:38 GMT [snip]
> That bit is fine, so far. [snip]
Thomas, Thanks once again for your generosity and forbearance.
Quite a lot to absorb. I've printed your last post and will now close the news reader, take a cup of coffee and a cigarette on the back porch, and see what I've to do.
Greatly appreciated. Jeff
:_)"Smoking it over"
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|