Initializing public static final variables in a constructor
I am trying to create a class Version
for my application that will read the version numbers from the manifest at boot time and then simply reference, for example, Version.MAJOR
and such wherever I need it elsewhere. However, I am having problems with this. Here is my current code:
public class Version {
public static final int APPCODE;
public static final int MAJOR;
public static final int MINOR;
public static final char RELEASE;
public static final int BUILD;
static {
try {
Class clazz = Version.class;
String className = clazz.getSimpleName() + ".class";
String classPath = clazz.getResource(className).toString();
if (classPath.startsWith("jar")) {
String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF";
Manifest manifest = new Manifest(new URL(manifestPath).openStream());
Attributes attr = manifest.getMainAttributes();
APPCODE = Integer.parseInt(attr.getValue("APPCODE"));
MAJOR = Integer.parseInt(attr.getValue("MAJOR"));
MINOR = Integer.parseInt(attr.getValue("MINOR"));
RELEASE = attr.getValue("RELEASE").charAt(0);
BUILD = Integer.parseInt(attr.getValue("BUILD"));
}
} catch (IOException e) {
System.exit(9001);
}
}
}
It won't compile because the variables static final
might not be initialized (for example if the wrong manifest is loaded or there is an exception loading it) and I can't figure out what's the correct procedure to do this.
Reading this question gave me some idea of ββnot using public static final
. Should I use public static
with getter methods?
source to share
If you make sure you always assign to fields final
exactly once, the compiler is happy:
public class Version {
public static final int APPCODE;
public static final int MAJOR;
public static final int MINOR;
public static final char RELEASE;
public static final int BUILD;
static {
int appcode = 0;
int major = 0;
int minor = 0;
char release = 0;
int build = 0;
try {
Class clazz = Version.class;
String className = clazz.getSimpleName() + ".class";
String classPath = clazz.getResource(className).toString();
if (classPath.startsWith("jar")) {
String manifestPath = classPath.substring(0,
classPath.lastIndexOf("!") + 1)
+ "/META-INF/MANIFEST.MF";
Manifest manifest = new Manifest(
new URL(manifestPath).openStream());
Attributes attr = manifest.getMainAttributes();
appcode = Integer.parseInt(attr.getValue("APPCODE"));
major = Integer.parseInt(attr.getValue("MAJOR"));
minor = Integer.parseInt(attr.getValue("MINOR"));
release = attr.getValue("RELEASE").charAt(0);
build = Integer.parseInt(attr.getValue("BUILD"));
}
} catch (IOException e) {
System.exit(9001);
}
APPCODE = appcode;
MAJOR = major;
MINOR = minor;
RELEASE = release;
BUILD = build;
}
}
source to share
you can remove the block of code associated with showing the manifest from the Version class and put it in a separate class - for example (ManifestReader) - and initialize the version instances directly with the actual values ββin the constructor.
I would change "public static final" to "private final" (not static) because if you have more than one instance of the version class, everyone should have their own appcode, major minor, etc.
next to getter () for accessing private final fields!
source to share