I was looking at another question about final variables and noticed that you can declare final variables without initializing them (a blank final variable). Is there a reason it is desirable to do this, and when is it advantageous?
问题:
回答1:
This is useful to create immutable objects:
public class Bla {
private final Color color;
public Bla(Color c) {this.color = c};
}
Bla is immutable (once created, it can't change because color is final). But you can still create various Blas by constructing them with various colors.
See also this question for example.
EDIT
Maybe worth adding that a "blank final" has a very specific meaning in Java, which seems to have created some confusion in the comments - cf the Java Language Specification 4.12.4:
A blank final is a final variable whose declaration lacks an initializer.
You then must assign that blank final variable in a constructor.
回答2:
The final property of class must have a value assigned before object is created. So the last point where you can assign value to them is constructor.
This is used often for immutable objects.
public class Foo {
private final Bar bar;
public Foo(Bar bar) {
this.bar = bar;
}
public Bar getBar() {
return new Bar(bar);
}
}
What wiki says about it
Defensive copying.
回答3:
You can do this when you do not known what the value will be prior to the instrumentation of a Object, it just needs to have a value assigned in its constructor.
This is how you make immutable objects and it is used in the builder pattern.
class Builder{
final BuilderContext context;
private Builder(BuilderContext context){
this.context=context;
}
public static Builder New(){
return new Builder(new BuilderContext());
}
回答4:
Blank final variables must be assigned "somewhere" in the constructor. A rather constructed example:
public class Test {
final int sign;
public Test(String upDown) {
if (upDown.equals("up")) {
sign = +1;
} else {
sign = -1;
}
}
}
回答5:
One case could be when you have a field which you want to declare final, but whose assignment may throw an exception and you want to be able to take action if that happens:
class A {
final URLConnection conn;
A(String url) {
try {
this.conn = new URL(url).openConnection();
} catch (IOException | MalformedURLException e) {
// Maybe this isn't fatal, so just handle the Exception
// here and move on happily
}
}
}
回答6:
Use a blank final variable inside a method to show that all code paths which use the variable assign that variable exactly once (or throw an exception). Java compilers will guarantee that a blank final variable is assigned before it is used.
Example code inside some method:
final Foo foo;
if (condition1()) {
foo = makeFoo(1);
} else if (condition2()) {
throw new BarException();
} else {
foo = makeFoo(-1);
}
...
blahBlahBlah(foo);
Using blank final variables tells the next reader of the code that the compiler guarantees that someone assigned foo before calling blahBlahBlah(foo).
The question asks about "blank final variables". Discussion of "blank final fields" is a different discussion, and interesting in its own right.
回答7:
From Wikipedia
The blank final, which was introduced in Java 1.1, is a final variable whose declaration lacks an initializer. A blank final can only be assigned once and must be unassigned when an assignment occurs. In order to do this, a Java compiler runs a flow analysis to ensure that, for every assignment to a blank final variable, the variable is definitely unassigned before the assignment; otherwise a compile-time error occurs.
In general, a Java compiler will ensure that the blank final is not used until it is assigned a value and that once assigned a value, the now final variable cannot be reassigned another value.
回答8:
noticed that you can declare
final
variables without initializing them
You have to initialize it later (for example in the constructor) so it wont stay empty.
回答9:
I find them very useful for methods that derive a state. It provides a clean execution path and makes sure the state variable is assigned once and only once. For example:
public boolean isEdible() {
final boolean edible;
if (vegetable) {
edible = true;
} else if (animal) {
if (vegetarian) {
edible = false;
} else {
edible = true;
}
}
System.out.println("Is edible: " + edible);
return edible;
}