Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java final field may already be assigned: compiler bug?

Tags:

java

final

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?

like image 909
user2609605 Avatar asked Jun 24 '26 20:06

user2609605


1 Answers

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 catch block iff all of the following are true:
    • V is definitely unassigned after the try block.

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;
}
like image 188
shmosel Avatar answered Jun 26 '26 09:06

shmosel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!