| Wiki Markup |
|---|
According to the Java Language Specification \[[JLS 05|AA. Java References#JLS 05]\], the keyword {{this}} denotes: |
When used as a primary expression, the keyword
thisdenotes a value that is a reference to the object for which the instance method was invoked (§15.12), or to the object being constructed. The type ofthisis the classCwithin which the keywordthisoccurs. At run time, the class of the actual object referred to may be the classCor any subclass ofC.
...
- Returning
thisfrom a non-private method - Passing
thisas a parameter to a method belonging to another class - Publishing
thisfrom the constructor of an object under construction - Publishing
thissuch that pieces of code beyond its current scope can obtain its reference - Calling a non-final method from a constructor (MET04-J. Ensure that constructors do not call overridable methods)
- Overriding the finalizer of a non-final class when its construction ceases (OBJ04-J. Do not allow partially initialized objects to be accessed). This is a special case of the previous point.
This guideline primarily focuses on the ill effects potential consequences of this escaping during object construction . Potential consequences include including race conditions and improper initialization.
In general, you must detect cases where the this reference can leak out beyond the scope of the current context. In particular, be careful when using public variables and methods.
Noncompliant Code Example
This noncompliant code example defines the ExceptionReporter interface that is implemented by the class ExceptionReporters. This class is useful for reporting exceptions after filtering out any sensitive information (EXC01-J. Use a class dedicated to reporting exceptions). The ExceptionReporters constructor of ExceptionReporters, incorrectly publishes the this reference before construction of the object has concluded. This is because it sets the exception reporter occurs in the last statement in the constructor (statement er.setExceptionReporter(this)) . It is misleading that, because which sets the exception reporter. Because it is the last statement in the constructor, it must this may be benign.
The class MyExceptionReporter subclasses ExceptionReporters with the intent of adding a logging mechanism that logs critical messages before an exception is reported. Its constructor invokes the superclass's constructor (a mandatory first step) which publishes the exception reporter, before the initialization of the subclass has concluded. Note that the subclass initialization consists of obtaining an instance of the default logger.
Consequently, the exception handler is set. If any exception occurs before the call to Logger.getLogger in the subclass, it is not logged. Instead, a NullPointerException is generated which may again get swallowed by the reporting mechanism.
In summary, this behavior is due to the race condition between an oncoming exception and the initialization of the subclass. If the exception comes too soon, it finds the subclass in a compromised state. This behavior is even more counter intuitive because logger is declared final and is not expected to contain an unintialized value.
misconstrued as benign.
| Code Block | ||
|---|---|---|
| ||
// Interface ExceptionReporter
public interface ExceptionReporter {
public void setExceptionReporter(ExceptionReporter er);
public void report(Throwable exception);
}
// Class ExceptionReporters
public class ExceptionReporters implements ExceptionReporter {
public ExceptionReporters(ExceptionReporter er) {
// Carry out initialization
// Incorrectly publishes the "this" reference
er.setExceptionReporter(this);
}
public void report(Throwable exception) { /* default implementation */ }
public final void setExceptionReporter(ExceptionReporter er) {
// Sets the reporter
}
}
// Class MyExceptionReporter derives from ExceptionReporters
public class MyExceptionReporter extends ExceptionReporters {
private final Logger logger;
public MyExceptionReporter(ExceptionReporter er) {
super(er); // Calls superclass's constructor
logger = Logger.getLogger("com.organization.Log");
}
public void report(Throwable t) {
logger.log(Level.FINEST,"Loggable exception occurred",t);
}
}
|
The class MyExceptionReporter subclasses ExceptionReporters with the intent of adding a logging mechanism that logs critical messages before an exception is reported. Its constructor invokes the superclass's constructor (a mandatory first step) which publishes the exception reporter before the initialization of the subclass has concluded, setting the exception handler. Note that the subclass initialization consists of obtaining an instance of the default logger.
If any exception occurs before the call to Logger.getLogger in the subclass, it is not logged. Instead, a NullPointerException is generated which may be consumed by the reporting mechanism.
This erroneous behavior results from the race condition between an oncoming exception and the initialization of the subclass. If the exception comes too soon, it finds the subclass in a compromised state. This behavior is even more counter intuitive because logger is declared final and is not expected to contain an unintialized value.
Compliant Solution
This compliant solution declares the setReporter() method in class MyExceptionReporter. It explicitly calls the superclass's setExceptionReporter() method, publishing a reference to its own Class object. It is not permissible to publish the reference in the constructor for MyExceptionReporter for reasons noted earlier in the noncompliant code example.
| Code Block | ||
|---|---|---|
| ||
public class MyExceptionReporter extends ExceptionReporters {
// ...
public void setReporter(ExceptionReporter er) {
super.setExceptionReporter(this);
}
}
|
...
Noncompliant Code Example
...