When I add an event listener using a lambda that calls an overridable method in the constructor, I get a warning. If I use a method reference, I don't get any warnings about overridable methods or leaking this. Should I avoid method references in the constructor or is it safe?
Here's a simple example:
public class SomeClass {
public SomeClass(SomeObj obj) {
obj.addListener(this::handleEvent); // no warnings, is it really safe?
obj.addListener((event) -> handleEvent(event)); // warning about overridable method in constructor
}
private void handleEvent(Event event) {
event.doSomething(someMethod());
}
private void someMethod() {
...
}
}
Calling instance method in constructor is dangerous as the object is not yet fully initialized (this applies mainly to methods than can be overridden). Also complex processing in constructor is known to have a negative impact on test-ability.
Yes, as mentioned we can call all the members of a class (methods, variables, and constructors) from instance methods or, constructors.
Constructor looks like method but it is not. It does not have a return type and its name is same as the class name. But, a constructor cannot be overridden.
clone() acts like a copy constructor. Typically it calls the clone() method of its superclass to obtain the copy, etc. until it eventually reaches Object 's clone() method. The special clone() method in the base class Object provides a standard mechanism for duplicating objects.
No, this is most definitely not safe. You are publishing this
where it may be accessed by alien code before the object has been fully constructed. Registering a listener from a constructor is always a no-no, whether you do it via an anonymous class or via a lambda or method reference. Your example is equivalent to the idiom I warned about in this article (12 years ago!): https://www.ibm.com/developerworks/library/j-jtp0618/
The involvement of method references here is a red herring; the problem is that you are making available a reference to a partially constructed object to code that you don't control.
The compiler can't warn you about everything; just because there's no warning doesn't mean your code is right :)
Method references and lambdas both evaluate to the same thing, a functional interface:
So, the two are essentially equivalent. In the case of the method reference, the target is explicitly this
, and in the case of the lambda, it's implicitly this
. So, their "warning-ness" is the same. The next question is: which one did the compiler get wrong? Is the warning wrong, or the lack of warning?
The reason the warning is there is that leaking this
from the constructor has a couple of big dangers. One relates to multithreading: any memory visibility guarantees you get from final
field semantics (as well as some other guarantees) are gone if you leak this
from the reference. The other concern is that the addListener
method will invoke the method on this
right away, before the constructor has finished. That is, it'll be invoking a method on a partially-constructed object. This is especially problematic for overridable methods, because it could be that this constructor is for somebody else's superclass, and that somebody else has overridden the method in question. In that case, you'll be invoking the method on that subclass, whose constructor hasn't even had a chance to start yet (since a superclass's constructor is run first).
So, yes, the warning on the lambda is correct. And it should be there for the method reference, too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With