Is the complexity needed to prevent downcasting from constructor to overridden method worth it?

Invoking non-final instance methods in constructors risks downcasting from a constructor to an overridden method as such:

public class Start {
    public static void main(String[] args) {
        try {
            ClassB obj = new ClassB();

        } catch(Exception e) { }
    }

        static class ClassA {
            ClassA() { initSomething(); }

            void initSomething() {
                System.out.println("ClassA:: initSomething()...");
            }
        }


        static class ClassB extends ClassA {
            @Override
            void initSomething() {
                System.out.println("ClassB:: initSomething()...");
            }
        }
}

You now rely on every subclass to invoke super.initSomething().

In a lot of the constructors I’ve been looking at, overridable instance methods are invoked. Also, I’ve never seen a method marked final.

Am I being stupidly pedantic by having a policy of not invoking non-final instance methods in constructors? In reality, such downcasting really is a corner case. What do experienced programmers do?

Am I being stupidly pedantic by having a policy of not invoking
non-final instance methods in constructors?

No, but it is important to understand construction in more detail.

In reality, such downcasting really is a corner case. What do
experienced programmers do?

I do not see any “downcasting” going on here, only invoking overridable methods.

The core tenet to keep in mind is that a constructor must finish with the object being constructed in a usable state. This extends to each “level” of an object: the superclass constructor must finish completely and correctly, then the subclass constructor runs and properly initializes its state.

The very idea of an “initialization” method is an anathema to object-oriented programming, because the constructor is the initialization method (even if constructors are not technically methods in Java). It makes no sense to rely on another method when you already have an initialization method.

If a constructor does use a method, it should only call final methods. Otherwise, a method could be overridden in a subclass. That overridden method could try to access state that is not yet initialized because the subclass constructor has not run other than to call super(). If the method does access uninitialized state, it breaks the rule above because the constructor has not really run yet. It breaks the ordering of construction from the superclass to each subclass.

Of course, like most “rules” in object-oriented methodology, they are more what you would call… guidelines. Follow them most of the time, but do not jump through ten hoops to avoid jumping through one. If it makes sense to break the “rule” then break it: but be careful, and test it.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *