Are all final variables captured by anonymous classes? - java

I thought I knew the answer to this, but I can't find any confirmation after an hour or so of searching.
In this code:
public class Outer {
// other code
private void method1() {
final SomeObject obj1 = new SomeObject(...);
final SomeObject obj2 = new SomeObject(...);
someManager.registerCallback(new SomeCallbackClass() {
#Override
public void onEvent() {
System.out.println(obj1.getName());
}
});
}
}
Assume that registerCallback saves its parameter somewhere, so that the object of the anonymous subclass will live for a while. Obviously this object has to maintain a reference to obj1 so that onEvent will work if it is called.
But given that the object doesn't use obj2, does it still maintain a reference to obj2, so that obj2 can't be garbage-collected while the object lives? I was under the impression that all visible final (or effectively final) local variables and parameters were captured and thus couldn't be GC'ed as long as the object was alive, but I can't find anything that says one way or the other.
Is it implementation-dependent?
Is there a section in the JLS that answers this? I wasn't able to find the answer there.

The language spec has very little to say about how anonymous classes should capture variables from their enclosing scope.
The only especially relevant section of the language spec that I can find is JLS Sec 8.1.3:
Any local variable, formal parameter, or exception parameter used but not declared in an inner class must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.)
(Anonymous classes are inner classes)
It does not specify anything about which variables the anonymous class should capture, or how that capturing should be implemented.
I think it is reasonable to infer from this that implementations need not capture variables that aren't referenced in the inner class; but it doesn't say they can't.

Only obj1 is captured.
Logically, the anonymous class is implemented as a normal class something like this:
class Anonymous1 extends SomeCallbackClass {
private final Outer _outer;
private final SomeObject obj1;
Anonymous1(Outer _outer, SomeObject obj1) {
this._outer = _outer;
this.obj1 = obj1;
}
#Override
public void onEvent() {
System.out.println(this.obj1.getName());
}
});
Note that an anonymous class is always an inner class, so it will always maintain a reference to the outer class, even if it doesn't need it. I don't know if later versions of the compiler have optimized that away, but I don't think so. It is a potential cause of memory leaks.
The use of it becomes:
someManager.registerCallback(new Anonymous1(this, obj1));
As you can see, the reference value of obj1 is copied (pass-by-value).
There is technically no reason for obj1 to be final, whether declared final or effectively final (Java 8+), except that if it wasn't and you change the value, the copy wouldn't change, causing bugs because you expected the value to change, given that the copying is a hidden action. To prevent programmer confusion, they decided that obj1 must be final, so you can never become confused about that behavior.

I was curious and surprised by your statement that much (why would compiler do such thing???), that I had to check it myself. So I made simple example like this
public class test {
private static Object holder;
private void method1() {
final Object obj1 = new Object();
final Object obj2 = new Object();
holder = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(obj1);
}
};
}
}
And resulted with following bytecode for of method1
private method1()V
L0
LINENUMBER 8 L0
NEW java/lang/Object
DUP
INVOKESPECIAL java/lang/Object.<init> ()V
ASTORE 1
L1
LINENUMBER 9 L1
NEW java/lang/Object
DUP
INVOKESPECIAL java/lang/Object.<init> ()V
ASTORE 2
L2
LINENUMBER 10 L2
NEW test$1
DUP
ALOAD 0
ALOAD 1
INVOKESPECIAL test$1.<init> (Ltest;Ljava/lang/Object;)V
PUTSTATIC test.holder : Ljava/lang/Object;
Which means:
L0 - store first final with idx 1 (ASTORE 1)
L1 - store second final with idx 2(that one is not used in anon class) (ASTORE 2)
L2 - create new test$1 with argumets (ALOAD 0) this and obj1 (ALOAD 1)
So I have no idea, how did you get to the conclusion that obj2 is passed to anonymous class instance, but it was simply wrong. IDK if it is compiler dependent, but as for what other has stated, it is not impossible.

obj2 will be garbage collected since it has no reference to it. obj1 will not be garbage collected as long as the event is active since even if you created an anonymous class, you've created a direct reference to obj1.
The only thing final does is that you can't re-define the value, it doesn't protect the object from the garbage collector

Related

Default Instance Value Initialization in java

I am trying to check for default value of instance variables (i.e. 0 here) in the generated bytecode.
I can see <init>() getting called and if I print myvar instance variable inside constructor then I see getfield called for myvar but then where was this default set first?
Please answer on following:
When is default value being set in myvar? (after compilation or object creation time)
Who(compiler or jvm) is initializing(or let's say setting default value) in the instance variable?
public class FieldInit {
int myvar;
public static void main(String[] args) {
new FieldInit(); // and what would happen if I comment out this
}
}
I am trying to deassemble bytecode using javap but not able to see the <clinit>() method, I guess here this may be happening. Please let me know if it is possible to see <clinit>() method and if so, how?
In JVM, an object instantiation is split into two bytecode instructions:
new allocates a new uninitialized object;
invokespecial calls a constructor that initializes the object.
The JVM Specification for the new bytecode says:
Memory for a new instance of that class is allocated from the
garbage-collected heap, and the instance variables of the new object
are initialized to their default initial values
The JVM sets all instance fields to zeroes when executing new instruction. So, by the time a constructor is invoked, all fields are already set to their default values. You will not find this "zeroing" in the bytecode - it's done implicitly by the JVM during object allocation.
Your question is not answerable because it's more complicated than you think it is.
fields in java are encoded in a field datastructure and this datastructure includes room for the initial value. However, the only possible initial values in this data structure are numbers and strings.
Let's see it in action! (and note that L is just a java syntax thing to tell java: This number is a long, not an int. 5 is a constant, so is 5L).
class Test {
public static final long mark = 5L;
}
javac Test.java
javap -v c Test
.....
public static final long mark;
descriptor: J
flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
ConstantValue: long 5l
Hey look there it is, constant value 5L.
But what if it isn't constant?
Ah, that's a problem. You can't encode that here.
So, instead, it's syntax sugar time!
You can write a special method in any class that is run during a class's static initialization. You can also write a special method that is run as a new instance is made. That one is almost entirely the same as a constructor, with only exotic differences. It looks like this:
public class Test {
static {
System.out.println("What voodoo magic is this?");
}
public static void main(String[] args) {
System.out.println("In main");
}
}
Let's see it in action!
javac Test.java
java Test
What voodoo magic is this?
In main
javap -c -v Test
...
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #7 // Field >java/lang/System.out:Ljava/io/PrintStream;
3: ldc #21 // String Voodoo...
5: invokevirtual #15 // Method >java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 3: 0
line 4: 8
}
As you can see, that weird static{} thing was compiled to something that looks exactly like a method, bytecode and all, but the name of the method is bizarre, it's just static{}.
Here comes the clue
But what happens if we make that a little more complicated. Let's initialize this field to the current time, instead!
class Test {
public static final long mark = System.currentTimeMillis();
}
This is just syntax sugar. That explains how this works, because as I told you, at the class file level, you cannot initialize fields with non-constants. Thus, that compiles to the same thing as:
class Test {
public static final long mark;
static {
mark = System.currentTimeMillis();
}
}
and you can javap to confirm this.
One is called a 'compile time constant'. The other isn't. This shows up in various ways. For example, you can pass a CTC as annotation parameter. Try that: try using static final long MARK = 5L; (you will be able to), then static final long MARK = System.currentTimeMillis(); - that won't be allowed.
So, where is that initial value? If it's a constant value, javap -c -v will show it. If it isn't, it's stuck in the static block.

How do inline anonymous classes in regard with local variables work in backround? [duplicate]

I thought I knew the answer to this, but I can't find any confirmation after an hour or so of searching.
In this code:
public class Outer {
// other code
private void method1() {
final SomeObject obj1 = new SomeObject(...);
final SomeObject obj2 = new SomeObject(...);
someManager.registerCallback(new SomeCallbackClass() {
#Override
public void onEvent() {
System.out.println(obj1.getName());
}
});
}
}
Assume that registerCallback saves its parameter somewhere, so that the object of the anonymous subclass will live for a while. Obviously this object has to maintain a reference to obj1 so that onEvent will work if it is called.
But given that the object doesn't use obj2, does it still maintain a reference to obj2, so that obj2 can't be garbage-collected while the object lives? I was under the impression that all visible final (or effectively final) local variables and parameters were captured and thus couldn't be GC'ed as long as the object was alive, but I can't find anything that says one way or the other.
Is it implementation-dependent?
Is there a section in the JLS that answers this? I wasn't able to find the answer there.
The language spec has very little to say about how anonymous classes should capture variables from their enclosing scope.
The only especially relevant section of the language spec that I can find is JLS Sec 8.1.3:
Any local variable, formal parameter, or exception parameter used but not declared in an inner class must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.)
(Anonymous classes are inner classes)
It does not specify anything about which variables the anonymous class should capture, or how that capturing should be implemented.
I think it is reasonable to infer from this that implementations need not capture variables that aren't referenced in the inner class; but it doesn't say they can't.
Only obj1 is captured.
Logically, the anonymous class is implemented as a normal class something like this:
class Anonymous1 extends SomeCallbackClass {
private final Outer _outer;
private final SomeObject obj1;
Anonymous1(Outer _outer, SomeObject obj1) {
this._outer = _outer;
this.obj1 = obj1;
}
#Override
public void onEvent() {
System.out.println(this.obj1.getName());
}
});
Note that an anonymous class is always an inner class, so it will always maintain a reference to the outer class, even if it doesn't need it. I don't know if later versions of the compiler have optimized that away, but I don't think so. It is a potential cause of memory leaks.
The use of it becomes:
someManager.registerCallback(new Anonymous1(this, obj1));
As you can see, the reference value of obj1 is copied (pass-by-value).
There is technically no reason for obj1 to be final, whether declared final or effectively final (Java 8+), except that if it wasn't and you change the value, the copy wouldn't change, causing bugs because you expected the value to change, given that the copying is a hidden action. To prevent programmer confusion, they decided that obj1 must be final, so you can never become confused about that behavior.
I was curious and surprised by your statement that much (why would compiler do such thing???), that I had to check it myself. So I made simple example like this
public class test {
private static Object holder;
private void method1() {
final Object obj1 = new Object();
final Object obj2 = new Object();
holder = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(obj1);
}
};
}
}
And resulted with following bytecode for of method1
private method1()V
L0
LINENUMBER 8 L0
NEW java/lang/Object
DUP
INVOKESPECIAL java/lang/Object.<init> ()V
ASTORE 1
L1
LINENUMBER 9 L1
NEW java/lang/Object
DUP
INVOKESPECIAL java/lang/Object.<init> ()V
ASTORE 2
L2
LINENUMBER 10 L2
NEW test$1
DUP
ALOAD 0
ALOAD 1
INVOKESPECIAL test$1.<init> (Ltest;Ljava/lang/Object;)V
PUTSTATIC test.holder : Ljava/lang/Object;
Which means:
L0 - store first final with idx 1 (ASTORE 1)
L1 - store second final with idx 2(that one is not used in anon class) (ASTORE 2)
L2 - create new test$1 with argumets (ALOAD 0) this and obj1 (ALOAD 1)
So I have no idea, how did you get to the conclusion that obj2 is passed to anonymous class instance, but it was simply wrong. IDK if it is compiler dependent, but as for what other has stated, it is not impossible.
obj2 will be garbage collected since it has no reference to it. obj1 will not be garbage collected as long as the event is active since even if you created an anonymous class, you've created a direct reference to obj1.
The only thing final does is that you can't re-define the value, it doesn't protect the object from the garbage collector

Why do we allow referring to an enum member of an enum member in Java?

Given the following enum:
enum Repeat {
Daily,
Weekly,
Yearly
}
I realize we are able to write it this way:
Repeat repeat = Repeat.Daily.Weekly.Yearly.Weekly;
which is equivalent to:
Repeat repeat = Repeat.Weekly;
May I know why such syntax is allowed? Is there a way to let the compiler warn us against this?
This is allowed as Daily, Weekly, Yearly are the static field by default inside the enum and holds the object of Repeat. Also, you will get a warning from the compiler "The static field Repeat.Weekly should be accessed in a static way". It is similar to below lines of code.
class Foo{
public static Foo obj1 = new Foo();
public static Foo obj2 = new Foo();
public static Foo obj3 = new Foo();
}
Foo f = Foo.obj1.obj2.obj3; // will work fine but you will get a warning from the compiler.
Here is some part of bytecode inspection of Repeat enum and from this, it is clear that Enum variable is static and holds the Object of Enum itself.
0: new #1 // class com/java8/demo/Repeat
3: dup
4: ldc #14 // String Daily
6: iconst_0
7: invokespecial #15 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #19 // Field Daily:Lcom/java8/demo/Repeat;
13: new #1 // class com/java8/demo/Repeat
Enum instance are just static instance of the enum class.
We have two way to access static field of a class:
Via class itselft: Repeat.Daily
Via instance of class: Repeat.Daily.Daily
When you chain your enum:
Repeat repeat = Repeat.Daily.Weekly.Yearly.Weekly;
It's just like get a static field from an instance of a class.
Is there a way to let compiler warn us against this?
Yes, use a good IDE and turn on warning. That way, you'll be notified as soon as your write the code, before you even compile it.
E.g. in Eclipse, it is called "Non-static access to static member":
Enum literals are static members, and with each static member, one can access them either using the class reference:
TypeName.staticMember
TypeName.staticMethod()
Or on an instance:
new TypeName().staticMember
new TypeName().staticMethod()
The second approach is discouraged (and the compiler will issue a warning)
As enum literals are just static members, Repeat.Daily.Weekly.Yearly.Weekly is like the second code snippet above, accessing static members on instance references.
With a class, that would be:
class Type {
static Type INSTANCE1, INSTANCE2, INSTANCE3;
}
And one can get a reference to INSTANCE3 using Type.INSTANCE1.INSTANCE2.INSTANCE3. It's valid, but it's bad practice.

Implied anonymous types inside lambdas

In this question, user #Holger provided an answer that shows an uncommon usage of anonymous classes, which I wasn't aware of.
That answer uses streams, but this question is not about streams, since this anonymous type construction can be used in other contexts, i.e.:
String s = "Digging into Java's intricacies";
Optional.of(new Object() { String field = s; })
.map(anonymous -> anonymous.field) // anonymous implied type
.ifPresent(System.out::println);
To my surprise, this compiles and prints the expected output.
Note: I'm well aware that, since ancient times, it is possible to construct an anonymous inner class and use its members as follows:
int result = new Object() { int incr(int i) {return i + 1; } }.incr(3);
System.out.println(result); // 4
However, this is not what I'm asking here. My case is different, because the anonymous type is propagated through the Optional method chain.
Now, I can imagine a very useful usage for this feature... Many times, I've needed to issue some map operation over a Stream pipeline while also preserving the original element, i.e. suppose I have a list of people:
public class Person {
Long id;
String name, lastName;
// getters, setters, hashCode, equals...
}
List<Person> people = ...;
And that I need to store a JSON representation of my Person instances in some repository, for which I need the JSON string for every Person instance, as well as each Person id:
public static String toJson(Object obj) {
String json = ...; // serialize obj with some JSON lib
return json;
}
people.stream()
.map(person -> toJson(person))
.forEach(json -> repository.add(ID, json)); // where's the ID?
In this example, I have lost the Person.id field, since I've transformed every person to its corresponding json string.
To circumvent this, I've seen many people use some sort of Holder class, or Pair, or even Tuple, or just AbstractMap.SimpleEntry:
people.stream()
.map(p -> new Pair<Long, String>(p.getId(), toJson(p)))
.forEach(pair -> repository.add(pair.getLeft(), pair.getRight()));
While this is good enough for this simple example, it still requires the existence of a generic Pair class. And if we need to propagate 3 values through the stream, I think we could use a Tuple3 class, etc. Using an array is also an option, however it's not type safe, unless all the values are of the same type.
So, using an implied anonymous type, the same code above could be rewritten as follows:
people.stream()
.map(p -> new Object() { Long id = p.getId(); String json = toJson(p); })
.forEach(it -> repository.add(it.id, it.json));
It is magic! Now we can have as many fields as desired, while also preserving type safety.
While testing this, I wasn't able to use the implied type in separate lines of code. If I modify my original code as follows:
String s = "Digging into Java's intricacies";
Optional<Object> optional = Optional.of(new Object() { String field = s; });
optional.map(anonymous -> anonymous.field)
.ifPresent(System.out::println);
I get a compilation error:
Error: java: cannot find symbol
symbol: variable field
location: variable anonymous of type java.lang.Object
And this is to be expected, because there's no member named field in the Object class.
So I would like to know:
Is this documented somewhere or is there something about this in the JLS?
What limitations does this have, if any?
Is it actually safe to write code like this?
Is there a shorthand syntax for this, or is this the best we can do?
This kind of usage has not been mentioned in the JLS, but, of course, the specification doesn’t work by enumerating all possibilities, the programming language offers. Instead, you have to apply the formal rules regarding types and they make no exceptions for anonymous types, in other words, the specification doesn’t say at any point, that the type of an expression has to fall back to the named super type in the case of anonymous classes.
Granted, I could have overlooked such a statement in the depths of the specification, but to me, it always looked natural that the only restriction regarding anonymous types stems from their anonymous nature, i.e. every language construct requiring referring to the type by name, can’t work with the type directly, so you have to pick a supertype.
So if the type of the expression new Object() { String field; } is the anonymous type containing the field “field”, not only the access new Object() { String field; }.field will work, but also Collections.singletonList(new Object() { String field; }).get(0).field, unless an explicit rule forbids it and consistently, the same applies to lambda expressions.
Starting with Java 10, you can use var to declare local variables whose type is inferred from the initializer. That way, you can now declare arbitrary local variables, not only lambda parameters, having the type of an anonymous class. E.g., the following works
var obj = new Object() { int i = 42; String s = "blah"; };
obj.i += 10;
System.out.println(obj.s);
Likewise, we can make the example of your question work:
var optional = Optional.of(new Object() { String field = s; });
optional.map(anonymous -> anonymous.field).ifPresent(System.out::println);
In this case, we can refer to the specification showing a similar example indicating that this is not an oversight but intended behavior:
var d = new Object() {}; // d has the type of the anonymous class
and another one hinting at the general possibility that a variable may have a non-denotable type:
var e = (CharSequence & Comparable<String>) "x";
// e has type CharSequence & Comparable<String>
That said, I have to warn about overusing the feature. Besides the readability concerns (you called it yourself an “uncommon usage”), each place where you use it, you are creating a distinct new class (compare to the “double brace initialization”). It’s not like an actual tuple type or unnamed type of other programming languages that would treat all occurrences of the same set of members equally.
Also, instances created like new Object() { String field = s; } consume twice as much memory as needed, as it will not only contain the declared fields, but also the captured values used to initialize the fields. In the new Object() { Long id = p.getId(); String json = toJson(p); } example, you pay for the storage of three references instead of two, as p has been captured. In a non-static context, anonymous inner class also always capture the surrounding this.
Absolutely not an answer, but more of 0.02$.
That is possible because lambdas give you a variable that is inferred by the compiler; it is inferred from the context. That is why it is only possible for types that are inferred, not for types we can declare.
The compiler can deduce the type as being anonymous, it's just that it can't express it so that we could use it by name. So the information is there, but due to the language restrictions we can't get to it.
It's like saying :
Stream<TypeICanUseButTypeICantName> // Stream<YouKnowWho>?
It does not work in your last example because you have obviously told the compiler the type to be : Optional<Object> optional thus breaking anonymous type inference.
These anonymous types are now (java-10 wise) available in a much simpler way too:
var x = new Object() {
int y;
int z;
};
int test = x.y;
Since var x is inferred by the compiler, int test = x.y; will work also
Is this documented somewhere or is there something about this in the JLS?
I think it is not a special case in anonymous class that need to introduced into JLS . as you have mentioned in your question you can access anonymous class members directly, e.g: incr(3).
First, let's look at a local class example instead, this will represent why chain with the anonymous class could access its members. for example:
#Test
void localClass() throws Throwable {
class Foo {
private String foo = "bar";
}
Foo it = new Foo();
assertThat(it.foo, equalTo("bar"));
}
As we can see, a local class members can be accessed out of its scope even if its members is private.
As #Holger has mentioned above in his answer, the compiler will create an inner class like as EnclosingClass${digit} for each anonymous class. so Object{...} has it own type that derived from Object. due to the chain methods return it own type EnclosingClass${digit} rather than the type which is derived from Object. this is why you chain the anonymous class instance could works fine.
#Test
void chainingAnonymousClassInstance() throws Throwable {
String foo = chain(new Object() { String foo = "bar"; }).foo;
assertThat(foo,equalTo("bar"));
}
private <T> T chain(T instance) {
return instance;
}
Due to we can't reference anonymous class directly, so when we break the chain methods into two lines we actually reference the type Object which is derived from.
AND the rest question #Holger has answered.
Edit
we can conclude that this construction is possible as long as the anonymous type is represented by a generic type variable?
I'm sorry I can't find the JLS reference again since my English is bad. but I can tell you that it does. you can using javap command to see the details. for example:
public class Main {
void test() {
int count = chain(new Object() { int count = 1; }).count;
}
<T> T chain(T it) {
return it;
}
}
and you can see that checkcast instruction has invoked below:
void test();
descriptor: ()V
0: aload_0
1: new #2 // class Main$1
4: dup
5: aload_0
6: invokespecial #3 // Method Main$1."<init>":(LMain;)V
9: invokevirtual #4 // Method chain:(Ljava/lang/Object;)Ljava/lang/Object;
12: checkcast #2 // class Main$1
15: getfield #5 // Field Main$1.count:I
18: istore_1
19: return

About reference to object before object's constructor is finished

Every one of you know about this feature of JMM, that sometimes reference to object could receive value before constructor of this object is finished.
In JLS7, p. 17.5 final Field Semantics we can also read:
The usage model for final fields is a simple one: Set the final fields
for an object in that object's constructor; and do not write a
reference to the object being constructed in a place where another
thread can see it before the object's constructor is finished. If this
is followed, then when the object is seen by another thread, that
thread will always see the correctly constructed version of that
object's final fields. (1)
And just after that in JLS the example follows, which demonstrate, how non-final field is not guaranteed to be initialized (1Example 17.5-1.1) (2):
class FinalFieldExample {
final int x;
int y;
static FinalFieldExample f;
public FinalFieldExample() {
x = 3;
y = 4;
}
static void writer() {
f = new FinalFieldExample();
}
static void reader() {
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // could see 0
}
}
}
Also, in this question-answer Mr. Gray wrote:
If you mark the field as final then the constructor is guaranteed to
finish initialization as part of the constructor. Otherwise you will
have to synchronize on a lock before using it. (3)
So, the question is:
1) According to statement (1) we should avoid sharing reference to immutable object before its constructor is finished
2) According to JLS's given example (2) and conclusion (3) it seems, that we can safely share reference to immutable object before its constructor is finished, i.e. when all its fields are final.
Isn't there some contradiction?
EDIT-1: What I exactly mean. If we will modify class in example such way, that field y will be also final (2):
class FinalFieldExample {
final int x;
final int y;
...
hence in reader() method it will be guaranteed, that:
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // guaranteed to see 4, isn't it???
If so, why we should avoid writing reference to object f before it's constructor is finished (according to (1)), when all fields of f are final?
Isn't there some contradiction [in the JLS around constructors and object publishing]?
I believe these are slightly different issues that are not contradictory.
The JLS reference is taking about storing an object reference in a place where other threads can see it before the constructor is finished. For example, in a constructor, you should not put an object into a static field that is used by other threads nor should you fork a thread.
public class FinalFieldExample {
public FinalFieldExample() {
...
// very bad idea because the constructor may not have finished
FinalFieldExample.f = this;
...
}
}
You shouldn't start the thread in a construtor either:
// obviously we should implement Runnable here
public class MyThread extends Thread {
public MyThread() {
...
// very bad idea because the constructor may not have finished
this.start();
}
}
Even if all of your fields are final in a class, sharing the reference to the object to another thread before the constructor finishes cannot guarantee that the fields have been set by the time the other threads start using the object.
My answer was talking about using an object without synchronization after the constructor had finished. It's a slightly different question although similar with regards to constructors, lack of synchronization, and reordering of operations by the compiler.
In JLS 17.5-1 they don't assign a static field inside of the constructor. They assign the static field in another static method:
static void writer() {
f = new FinalFieldExample();
}
This is the critical difference.
In the full example
class FinalFieldExample {
final int x;
int y;
static FinalFieldExample f;
public FinalFieldExample() {
x = 3;
y = 4;
}
static void writer() {
f = new FinalFieldExample();
}
static void reader() {
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // could see 0
}
}
}
As you can see, f is not set until after the constructor returns. This means f.x is safe because it is final AND the constructor has returned.
In the following example, neither value is guarenteed to be set.
class FinalFieldExample {
final int x;
int y;
static FinalFieldExample f;
public FinalFieldExample() {
x = 3;
y = 4;
f = this; // assign before finished.
}
static void writer() {
new FinalFieldExample();
}
static void reader() {
if (f != null) {
int i = f.x; // not guaranteed to see 3
int j = f.y; // could see 0
}
}
}
According to statement (1) we should avoid sharing reference to immutable object before its constructor is finished
You should not allow a reference to an object escape before it is constructed for a number of reason (immutable or other wise) e.g. the object might throw an Exception after you have store the object.
According to JLS's given example (2) and conclusion (3) it seems, that we can safely share reference to immutable object, i.e. when all its fields are final.
You can safely share a reference to an immutable object between threads after the object has been constructed.
Note: you can see the value of an immutable field before it is set in a method called by a constructor.
Construct exit plays an important role here; the JLS says "A freeze action on final field f of o takes place when c exits". Publishing the reference before/after constructor exit are very different.
Informally
1 constructor enter{
2 assign final field
3 publish this
4 }constructor exit
5 publish the newly constructed object
[2] cannot be reordered beyond constructor exit. so [2] cannot be reordered after [5].
but [2] can be reordered after [3].
Statement 1) does not say what you think it does. If anything, I would rephrase your statement:
1) According to statement (1) we should avoid sharing reference to
immutable object before its constructor is finished
to read
1) According to statement (1) we should avoid sharing reference to
mutable object before its constructor is finished
where what I mean by mutable is an object that has ANY non-final fields or final references to mutable objects. (have to admit I'm not 100% that you need to worry about final references to mutable objects, but I think I'm right...)
To put it another way, you should distinguish between:
final fields (immutable parts of a possibly immutable object)
non-final fields who have to be initialized before anyone interacts with this object
non-final fields that do not have to be initialized before anyone interacts with this object
The second one is the problem spot.
So, you can share references to immutable objects (all fields are final), but you need to use caution with objects that have non-final fields that HAVE to be initialized before the object can be used by anyone.
In other words, for the edited JLS example you posted where both fields are final, int j = f.y; is guaranteed to be final. But what that means is that you do NOT need to avoid writing the reference to object f, because it'll always be in a correctly initialized state before anyone could see it. You do not need to worry about it, the JVM does.

Categories