I have the following class:
public class FileId {
final long length;
final String hash;
FileId(File file) {
assert !file.isDirectory();
this.length = file.length();
try {
MessageDigest md = MessageDigest.getInstance("MD5");
try (FileInputStream fis = new FileInputStream(file)) {
byte[] dataBytes = new byte[1024];
int nread = 0;
while ((nread = fis.read(dataBytes)) != -1) {
md.update(dataBytes, 0, nread);
}
this.hash = md.toString();
} catch (IOException e) {
// TBD: add warning
this.hash = "";
}
} catch (NoSuchAlgorithmException nsae) {
// TBD: emit warning
this.hash = "";
}
}
}
The last two assignments of hash yield a compiler error
variable hash might already have been assigned
How can this be?
For example, if the first assignment is done, then no exceptions are thrown
so the other assignments do not occur.
Where is my mistake?
You're not wrong, but you're applying a deeper analysis than the compiler does. try and catch are not exclusive blocks by their nature, so the compiler wants to confirm that there was no assignment at all in the try. In the words of the JLS:
- V is definitely unassigned before a
catchblock iff all of the following are true:
- V is definitely unassigned after the
tryblock.
Since you have an assignment in the try block, your variable does not meet the requirement that final fields be definitely unassigned before assignment.
Practically speaking, there's a simple workaround. Create a non-final local variable and assign it to your field at the end of the constructor. You can even default it to the fallback so there's no need to overwrite it in the catch block.
FileId(File file) {
String hash = "";
try {
...
hash = md.toString();
} catch (...) {
// warning
}
this.hash = hash;
}
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