Reputation: 836
the noncompliant code example "inner class" section
it seems that there no instance method is called in the inner class, so I don't know how this reference escaped in this snippet.
I've readed some problems, like this, but it is different
public class DefaultExceptionReporter implements ExceptionReporter {
public DefaultExceptionReporter(ExceptionReporter er) {
er.setExceptionReporter(new ExceptionReporter() {
public void report(Throwable t) {
// report exception
}
public void setExceptionReporter(ExceptionReporter er) {
// register ExceptionReporter
}
});
}
// Default implementations of setExceptionReporter() and report()
}
Upvotes: 0
Views: 109
Reputation: 16920
Technically in this example you let this
reference escape.
Let's imagine that :
ExceptionReporter
instance that is passed to the constructor of DefaultExceptionReporter
DefaultExceptionReporter.this
in methods' of your anonymous class instance (for example you want to access field or invoke a method)In this case other thread might invoke methods on the ExceptionReporter
passed to the constructor of DefaultExceptionReporter
. And if those methods invoke methods from the instance that is set through setExceptionReporter
- other thread can potentially access
DefaultExceptionReporter
before it's instance is fully created. The access from other Thread
could be done through chain of references :
ExceptionReporter
(passed to the constructor) -> ExceptionReporter
(anonymous) -> DefaultExceptionReporter.this
.
Upvotes: 0
Reputation: 3311
This happens because when DefaultExceptionReporter
publishes the anonymous class, it implicitly publishes the enclosing DefaultExceptionReporter
instance as well. You can check this by writing a simple program to actually access that instance:
public static void main(String[] args) {
ExceptionReporter rep = new DefaultExceptionReporter(new ExceptionReporter() {
@Override
public void setExceptionReporter(ExceptionReporter er) {
for (Field field : er.getClass().getDeclaredFields()) {
System.out.println(field);
}
}
@Override
public void report() { }
});
}
Output:
final my.package.DefaultExceptionReporter my.package.DefaultExceptionReporter$1.this$0
This is because an anonymous class is always a non-static inner class (see reference) and these kinds of classes always have an implicit this
reference to the enclosing class.
Upvotes: 0
Reputation: 44240
In the constructor of DefaultExceptionReporter
, you instantiate an anonymous class. The anonymous class gets a reference to its parent class before the parent class is fully instantiated.
If the parent object had state, it would mean that the anonymous class could theoretically operate on it before it was fully constructed.
Hopefully this demonstrates the potential issue:
class DefaultExceptionReporter implements ExceptionReporter {
private final int foo;
public DefaultExceptionReporter(ExceptionReporter er) {
er.setExceptionReporter(new ExceptionReporter() {
{
System.out.println(DefaultExceptionReporter.this.foo);
}
public void report(Throwable t) {}
public void setExceptionReporter(ExceptionReporter er) {}
});
foo = 1;
}
// ...
}
This will print zero, even though foo
is final and assigned 1. A final variable has ended up having two values which should never normally be possible.
Because your object is stateless, I don't think it's a big deal. You should probably declare the class as final
though, so it's not possible to extend it and add state.
Upvotes: 1