【问题标题】:How to enforce a constructor to use the classes own methods in java如何强制构造函数在java中使用类自己的方法
【发布时间】:2017-02-07 20:00:04
【问题描述】:

假设从子类调用超类的构造函数,而子类隐藏了构造函数调用的超类的方法。然后java在超类的构造函数中使用来自子类的方法。但在大多数情况下,这并不是我们想要的行为,因为超类在设计时并没有考虑到子类——情况恰恰相反。

如何在不使它们成为最终或静态、重命名它们或将代码复制到构造函数中的情况下强制使用超类自己的方法进行初始化? (或者 java 不支持/不鼓励在构造函数中使用可覆盖的方法?如果是,那为什么?这种不令人满意的行为的隐藏原理是什么?)

我尝试了显式转换...

((SuperClass) this).method()

...这不起作用。

例子:

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  }

这会导致:

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)

编辑:一些进一步的测试和探索发现有一个奇怪的解决方案。将超类拆分为两部分并在构造函数中使用 super.method() 似乎给了 java 解释器一个足够清晰的标志,让他理清正确的方法。

例子:

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.");
    }
}

现在...

SubClass mySubClass = new SubClass();

...计算为:

I am the super class.

【问题讨论】:

  • “Java 不支持/不鼓励在构造函数中使用可覆盖的方法” - Yes, it does discourage it
  • 你想看看this question。简短的回答:在 Java 中是不可能的。也许有办法通过反射的方式获取隐藏的方法,但这并不高效且难以维护。
  • 如果你真的需要调用被覆盖的方法,你总是可以在你的子类的构造函数中使用 super.method()。
  • @dsp_user 这不是重点。如果有人扩展您的课程,他/她可能会忘记致电 super.method() 从而破坏您的代码。
  • 有人可能会争辩说,如果派生类必须调用被覆盖的方法,则应该将此功能移至基类。然后,基类应确保始终调用被覆盖的方法。不过,这可能无法回答最初的问题。

标签: java inheritance methods constructor


【解决方案1】:

在可扩展类中调用可覆盖的方法是绝对不能做的:

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";
    }
}

通常容器类在此类“init”方法中“初始化”,出现 NullPointerException 或丢失初始化结果(此处为 s)。

解决办法:

  • 如你所说的私有和最终方法
  • 如果您有多个构造函数,请调用 this(...) 并在一个构造函数中收集所有详细的初始化。

所以没有初始化方法。对于具有复杂初始化的构造函数,它也可能是代码异味,因此难以进行单元测试。然后构造函数不应该做太多,一个额外的方法应该做的工作:void open(...) 或类似的。 尤其是当有多个构造函数时。

【讨论】:

  • 基本问题似乎是,java中的方法绑定总是指实际的对象。 java中没有继承范围之类的东西。
  • @Koudela 是的,一种new Super(+extra room) 然后升级类。
猜你喜欢
  • 2018-10-19
  • 1970-01-01
  • 2013-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-11
  • 2011-04-17
相关资源
最近更新 更多