Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is using a reference to an overridable method in the constructor safe? [duplicate]

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() {
        ...
    }
}
like image 918
Dave Wolfe Avatar asked Jul 01 '14 18:07

Dave Wolfe


People also ask

Is it bad practice to call methods in constructor?

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.

Is it OK to call method from constructor Java?

Yes, as mentioned we can call all the members of a class (methods, variables, and constructors) from instance methods or, constructors.

Can constructor be overriden?

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.

What is the alternative for copy constructor in Java?

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.


2 Answers

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 :)

like image 195
Brian Goetz Avatar answered Oct 06 '22 19:10

Brian Goetz


Method references and lambdas both evaluate to the same thing, a functional interface:

  • method references are defined in JLS 15.13
  • lambdas are defined in JLS 15.27
  • functional interfaces are defined in JLS 9.8

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.

like image 38
yshavit Avatar answered Oct 06 '22 19:10

yshavit