0

Suppose the constructor of a superclass get called from a subclass and the subclass hides a method of the superclass the constructor calls. Then java uses the method from the subclass in the constructor of the superclass. But in most cases this is not the desired behavior, because the superclass gets not designed with the subclass in mind - it is the other way around.

How can one enforce the initialization with the superclasses own methods without making them final or static, renaming them or duplicating the code into the constructor? (Or does java not support/discourage using overridable methods in constuctors? If yes, then why? What is the hidden principle of this unsatisfying behavior?)

I tried an explicit cast...

((SuperClass) this).method()

... this does not work.

Example:

3   class SuperClass {
4       private int[] valField = new int[10];
5   
6       SuperClass(int[] cloneField) {
7           for (int i = 0; i < 10; i++) set(i, cloneField[i]);
8       }
9   
10      void set(int pos, int val) {
11          if (val < 0) throw new IllegalArgumentException("val must be positive; val was " + val);
12          if (pos < 0 || pos > 10) throw new IllegalArgumentException("pos must be within [0, 10]; pos was " + pos);
13          valField[pos] = val;
14      }
15  
16      int get(int pos) {
17          return valField[pos];
18      }
20  }


6   class SubClass extends SuperClass {
7       private Set<Integer> isZero = new HashSet();
8   
9       SubClass(int[] cloneField) {
10          super(cloneField);
11          isZero.clear();
12          for (int i = 0; i < 10; i++) 
13              if (this.get(i) == 0) 
14                  isZero.add(i); 
15      }
16      
17      void set(int pos, int val) {
18          super.set(pos, val);
19          if (val == 0) isZero.add(pos);
20          else isZero.remove(pos);
21      }
22  }

This results in:

Exception in thread "main" java.lang.NullPointerException
    at testingJava.SubClass.set(SubClass.java:19)
    at testingJava.SuperClass.<init>(SuperClass.java:7)
    at testingJava.SubClass.<init>(SubClass.java:10)
    at testingJava.MainClass.main(MainClass.java:9)

EDIT: Some further testing and exploration revealed there is a weird solution. Splitting the superclass in two parts and using super.method() in the constructor seems to give the java interpreter a clear enough sign to let him sort out the right method.

Example:

class SuperClass {
    void message() {
        System.out.println("I am the super class.");
    }
}

class MiddleClass extends SuperClass {
    MiddleClass() {
        super.message();
    }
}

class SubClass extends MiddleClass {
    void message() {
        System.out.println("I am the sub class.");
    }
}

Now...

SubClass mySubClass = new SubClass();

...evaluates to:

I am the super class.
Koudela
  • 181
  • 6
  • 4
    "does java not support/discourage using overridable methods in constuctors" - [Yes, it does discourage it](http://stackoverflow.com/questions/3404301/whats-wrong-with-overridable-method-calls-in-constructors) – resueman Sep 29 '16 at 13:11
  • 1
    You ma want to look at [this question](http://stackoverflow.com/a/3404369/4216641). Short answer: is is not possible in Java. Maybe there is a way to get the hidden method by means of reflection, but this is not performant and hard to maintain. – Turing85 Sep 29 '16 at 13:11
  • You can always use super.method() in your subclass's constructor if you really need to call the overriden method. – dsp_user Sep 29 '16 at 13:23
  • @dsp_user this is not the point. If someone extends your class, he/she might forget to call `super.method()` and thus break your code. – Turing85 Sep 29 '16 at 13:28
  • One could argue that if derived classes must call the overriden method, this functionality should be moved to the base class. The base class should then make sure that the overriden method is always called. This may not answer the original question though. – dsp_user Sep 29 '16 at 13:38
  • @resueman and Turing85: Thx for the links. I think the [Builder Pattern](http://stackoverflow.com/questions/328496/when-would-you-use-the-builder-pattern) can circumvent the problem, because _this_ always refers to the nested class. Unluckily I figured out that the problem is not bound to constructors. Any time a method uses it's own methods or the methods of a superclass, a subclass overwriting the used methods can break the programming logic. – Koudela Sep 30 '16 at 05:59

1 Answers1

1

Calling an overridable method in an extendable class one should never do:

class A {
    A() {
        init();
    }
    void init() {
    }
}

class B extends A {
    String s = "";
    String t = null;
    String u;

    B() {
        // - s, t and u are zeroed (null)
        // - super() is called
        // - - init() assign "sss" to s, "ttt" to t and "uuu" to u
        // - field initialisations are done for initialized fields
        //   assigns "" to s and null to t.
        System.out.printf("s=%s, t=%s, u=%s%n", s, t, u);
        // s=, t=null, u=uuu
    }

    @Override
    void init() {
        s = "sss";
        t = "ttt";
        u = "uuu";
    }
}

As typically container classes are "initialized" in such "init" methods, a NullPointerException, or lost initialisation results (here of s).

The solution:

  • private and final methods only as you said
  • if you have several constructors call this(...) and collect all elaborate initialisation in one single constructor.

So no init methods. It can also be a code smell, for a constructor with complex initialisation which then is hard to unit test. Then the constructor should not do much, and an additional method should do the work: void open(...) or such. Especially when there are several constructors.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • 1
    The base problem seems to be, the method binding in java always refers to the actual object. There is no such thing as an inheritance scope in java. – Koudela Sep 30 '16 at 06:22
  • @Koudela yes, a kind `new Super(+extra room)` and then upgrade the class. – Joop Eggen Sep 30 '16 at 06:49