I am writing a parser for a binary file format. I have created classes to represent the deserialized structures I am reading, in which I would like to use final variables to hold the extracted data.
class MyObject {
final int my_x;
final int my_y;
final int my_z;
}
The bump I am running into is that the presence of certain fields depends on certain flags being set. For example:
MyObject(InputStream is) {
my_x = in.read();
if (my_x == 1) {
my_y = in.read();
if (my_y == 1) {
my_z = in.read();
}
}
}
However, this gives me an error because my_y and my_z may not be initialized. These conditionals can be 5-6 levels deep, and I don't want to track which fields may not be read at each level of the branch tree. Another complication is that, based on certain flags, there may be subobjects that I would like to handle with the same pattern as the top-level structures.
class MyObject {
final int my_x;
final SubObject my_subobject;
MyObject(InputStream is) {
my_x = is.read();
if (my_x == 1)
my_subobject = new SubObject(is);
}
class SubObject {
final int sub_x;
final int sub_y;
SubObject(InputStream is) {
sub_x = is.read();
if (sub_x == 1)
sub_y = is.read();
}
}
}
Is there any way to make my fields final without twisting the code to handle each possible combination of flags?
Use local variables and assign to the final
fields at the end of the constructor.
public MyObject(InputStream is) {
int x = default_x_value;
int y = default_y_value;
int z = default_z_value;
x = in.read();
if (x == 1) {
y = in.read();
if (y == 1) {
z = in.read();
}
}
my_x = x;
my_y = y;
my_z = z;
}
Alternatively (as Jon Skeet suggests in his comment), use a static factory method that computes the appropriate values for a no-default constructor:
public static MyObject makeMyObject(InputStream is) {
int x = default_x_value;
int y = default_y_value;
int z = default_z_value;
x = in.read();
if (x == 1) {
y = in.read();
if (y == 1) {
z = in.read();
}
}
return new MyObject(x, y, z);
}
A third approach would be to define an initializer object class:
public class MyObject {
private static class MyObjectInitializer {
int x = default_x_value;
int y = default_y_value;
int z = default_z_value;
MyObjectInitializer(InputStream is) {
x = in.read();
if (x == 1) {
y = in.read();
if (y == 1) {
z = in.read();
}
}
}
}
public MyObject(InputStream is) {
this(new MyObjectInitializer(is));
}
private MyObject(MyObjectInitializer init) {
my_x = init.x;
my_y = init.y;
my_z = init.z;
}
}
The initializer class may have utility on its own, in which case you could make it (and the corresponding MyObject
constructor) public.