Reputation: 187
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?
Upvotes: 2
Views: 98
Reputation: 234795
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.
Upvotes: 4