Allowing the this reference to escape
JavaConcurrencyJava Problem Overview
I would appreciate help in understanding the following from 'Java Concurrency in Practice':
> Calling an overrideable instance method(one that is neither > private nor final) from the constructor can also allow the > this reference to escape.
- Does 'escape' here simply mean that we may probably be calling an instance method,before the instance is fully constructed?
I do not see 'this' escaping the scope of the instance in any other way. - How does 'final' prevent this from happening?Is there some aspect of 'final' in instance creation that I am missing?
Java Solutions
Solution 1 - Java
-
It means calling code outside the class, and passing
this
.
That code will assume that the instance is fully initialized, and may break if it isn't.
Similarly, your class might assume that some methods will only be called after the instance is fully initialized, but the external code is likely to break those assumptions. -
final
methods cannot be overridden, so you can trust them to not passthis
around.
If you call any non-final
method in the constructor for a non-final
class, a derived class might override that method and passthis
anywhere.
Even when you callfinal
methods, you still need to make sure that they are safely written – that they do not passthis
anywhere, and that themselves don't call any non-final
methods.
Solution 2 - Java
"Escape" means that a reference to the partially-constructed this
object might be passed to some other object in the system. Consider this scenario:
public Foo {
public Foo() {
setup();
}
protected void setup() {
// do stuff
}
}
public Bar extends Foo implements SomeListener {
@Override protected void setup() {
otherObject.addListener(this);
}
}
The problem is that the new Bar
object is being registered with otherObject
before its construction is completed. Now if otherObject
starts calling methods on barObject
, fields might not have been initialized, or barObject
might otherwise be in an inconsistent state. A reference to the barObject
(this
to itself) has "escaped" into the rest of the system before it's ready.
Instead, if the setup()
method is final
on Foo
, the Bar
class can't put code in there that will make the object visible before the Foo
constructor finishes.
Solution 3 - Java
I believe the example is something like
public class Foo {
public Foo() {
doSomething();
}
public void doSomething() {
System.out.println("do something acceptable");
}
}
public class Bar extends Foo {
public void doSomething() {
System.out.println("yolo");
Zoom zoom = new Zoom(this); // at this point 'this' might not be fully initialized
}
}
Because the super constructor is always called first (either implicitly or explicitly), the doSomething
will always get called for a child class. Because the above method is neither final
nor private
, you can override it in a child class and do whatever you want, which may conflict with what Foo#doSomething()
was meant to do.
Solution 4 - Java
Per secure coding
Example BAD code:
final class Publisher {
public static volatile Publisher published;
int num;
Publisher(int number) {
published = this;
// Initialization
this.num = number;
// ...
}
}
If an object's initialization (and consequently, its construction) depends on a security check within the constructor, the security check can be bypassed when an untrusted caller obtains the partially initialized instance. See rule OBJ11-J. Be wary of letting constructors throw exceptions for more information.
final class Publisher {
public static Publisher published;
int num;
Publisher(int number) {
// Initialization
this.num = number;
// ...
published = this;
}
}
> Because the field is nonvolatile and nonfinal, the statements within > the constructor can be reordered by the compiler in such a way that > the this reference is published before the initialization statements > have executed.
Correct code:
final class Publisher {
static volatile Publisher published;
int num;
Publisher(int number) {
// Initialization
this.num = number;
// ...
published = this;
}
}
> The this reference is said to have escaped when it is made available > beyond its current scope. Following are common ways by which the this > reference can escape: > > Returning this from a non-private, overridable method that is invoked from the constructor of a class whose object is being > constructed. (For more information, see rule MET05-J. Ensure that > constructors do not call overridable methods.) > Returning this from a nonprivate method of a mutable class, which allows the caller to manipulate the object's state indirectly. This > commonly occurs in method-chaining implementations; see rule VNA04-J. > Ensure that calls to chained methods are atomic for more information. > Passing this as an argument to an alien method invoked from the constructor of a class whose object is being constructed. > Using inner classes. An inner class implicitly holds a reference to the instance of its outer class unless the inner class is declared > static. > Publishing by assigning this to a public static variable from the constructor of a class whose object is being constructed. > Throwing an exception from a constructor. Doing so may cause code to be vulnerable to a finalizer attack; see rule OBJ11-J. Be wary of > letting constructors throw exceptions for more information. > Passing internal object state to an alien method. This enables the method to retrieve the this reference of the internal member object. > > This rule describes the potential consequences of allowing the this > reference to escape during object construction, including race > conditions and improper initialization. For example, declaring a field > final ordinarily ensures that all threads see the field in a fully > initialized state; however, allowing the this reference to escape > during object construction can expose the field to other threads in an > uninitialized or partially initialized state. Rule TSM03-J. Do not > publish partially initialized objects, which describes the guarantees > provided by various mechanisms for safe publication, relies on > conformance to this rule. Consequently, programs must not allow the > this reference to escape during object construction. > > In general, it is important to detect cases in which the this > reference can leak out beyond the scope of the current context. In > particular, public variables and methods should be carefully > scrutinized.