【问题标题】:How do I force a polymorphic call to the super method?如何强制对超级方法进行多态调用?
【发布时间】:2011-09-02 19:15:54
【问题描述】:

我有一个在广泛的层次结构中使用和覆盖的 init 方法。然而,每个 init 调用都扩展了前一个所做的工作。所以很自然,我会:

@Override public void init() {
   super.init();
}

这自然会确保所有内容都被调用和实例化。我想知道的是:我可以创建一种方法来确保调用超级方法吗?如果所有的 init 都没有被调用,那么对象中就会出现故障,所以如果有人忘记调用 super,我想抛出异常或错误。

TYFT~艾顿

【问题讨论】:

  • @Ted - 您发布的第一个链接看起来不像是重复的,另一个问题涉及 eclipse 的自动生成的方法存根,另一个问题是关于 c# 而不是 java。
  • @Andreas - 第一个答案显示了几种强制调用超类的方法。第二个被标记为 c# 和 Java,答案是混合解决方案。
  • 你可能可以为这样的东西创建自己的注释。
  • @Amir - 请原谅我的无知,但我将如何创建注释来做这样的事情?一般来说,我对注释非常陌生。

标签: java polymorphism super


【解决方案1】:

与其尝试那样做——顺便说一句,我不认为这是可以实现的! -- 换一种方法怎么样:

abstract class Base {
 public final void baseFunction() {
   ...
   overridenFunction(); //call the function in your base class
   ...
 }

 public abstract void overridenFunction();
}
...
class Child extends Base {
 public void overridenFunction() {...};
}

...
Base object = new Child();
object.baseFunction(); //this now calls your base class function and the overridenFunction in the child class!

这对你有用吗?

【讨论】:

  • 同样的事情可以通过重写 overridenFunction 和 super 调用多态化的 overridenFunction 来实现。创建 baseFunction 方法没有意义。
  • 除非我完全错过了什么。不太可能。 :)
  • 这不适用于第二代派生类。如果你定义了class Grandchild extends Child,那么你不能保证Grandchild.overridenFunction会调用到Child.overridenFunction
  • @Ted - 对,这就是我要解决的问题。如果Class 调用foo,然后ChildClass 覆盖foo 并且未能调用super.foo,则当GrandChildClass 尝试调用super.foo 时,愚蠢的层次结构崩溃。这就是我想要阻止的。
  • @Ted,我认为最好的方法是使用不同的名称,比如这里的 grandchildInit()。 Java 中唯一可以执行 OP 所需行为的情况是 ctor 链。如果对象不能在构造点初始化,我们得到这个设置。见en.wikipedia.org/wiki/Call_super
【解决方案2】:

如果派生类未能调用超类,以下是引发异常的一种方法:

public class Base {
    private boolean called;
    public Base() { // doesn't have to be the c'tor; works elsewhere as well
        called = false;
        init();
        if (!called) {
            // throw an exception
        }
    }
    protected void init() {
        called = true;
        // other stuff
    }
}

【讨论】:

  • 我喜欢这个。我认为,它需要进行一些调整才能进入我正在做的事情并且它仍然是运行时的,但我认为如果我希望实现的目标不可能实现,这将非常有效。
  • 我认为一般问题没有编译时解决方案。如果唯一关心的是基类初始化逻辑被执行,其他人提出的使用final init 方法的解决方案有效。这是我所知道的唯一一种检查派生类的方法是否调用了它的super 方法的方法。 (我想即使是明确调用Base.this.init() 的孙子类也可以打破这一点,从而跳过Child.init()。但这只是邪恶的。)
  • 确实如此。我认为这(和类似的)是更好的解决方案。谢谢。
【解决方案3】:

Android 实际上在 Activity 类中完成了这项工作。我不确定他们如何或是否必须为它构建运行时支持,但我会查看Activity 类实现的开源代码。具体来说,在任何生命周期方法中,您必须在执行任何操作之前调用相应的超类方法,否则它会抛出 SuperNotCalledException

例如,在onCreate(),你要做的第一件事就是调用super.onCreate()

【讨论】:

【解决方案4】:

我经常喜欢使用这个解决方案。它不会抛出运行时错误,但会显示语法错误:

 @CallSuper
 public void init() {
     // do stuff
 }

这是 Android 支持注释的一部分。

【讨论】:

    【解决方案5】:

    使继承树顶部的类在初始化时设置一个标志。然后继承树底部的一个类可以检查该标志以查看是否已遍历整个树。我会记录base 的每个孩子都应该包含以下初始化代码:

    super.init()
    if (!_baseIsInitialized) {
        // throw exception or do w/e you wish
    }
    

    基础用途

    _baseIsInitialized = true;
    

    反过来,强迫你的孩子打电话给super.init() 的做法要好得多,而且很可能包括丑陋的黑客行为。

    【讨论】:

    • 但是如何强制孩子检查
    • 有趣的解决方案。这是唯一的方法吗?我希望(也许是天真地)有更优雅的东西。
    • @Jigar Joshi:虽然是这样。
    • @Jigar:我认为这并不容易,而且在编译时根本不可能。告诉编译器是不可能的:“在标记 child.init 之后检查它是否包含 super.init(),如果没有则抛出错误”。
    【解决方案6】:

    我不知道有什么方法可以用方法做到这一点。

    但是,请注意,这正是构造函数的工作方式。每个构造函数都必须直接或间接调用其超类的构造函数之一。这是静态保证的。

    我注意到您正在编写一个 init 方法。你能重构让你的代码使用构造函数而不是 init 方法吗?这会让你一开始就有这种行为。无论如何,有些人(例如我)更喜欢构造函数而不是 init 方法,部分原因就是这个原因。

    请注意,使用构造函数而不是 init 方法可能并不意味着在您当前正在查看的类上使用它们 - 可能会进行重构,将需要初始化的状态移到可以使用适当构造函数的并行类层次结构中。

    【讨论】:

      【解决方案7】:

      现在您可以使用@CallSuper 注释您的方法。这将 Lint 检查对该方法的任何覆盖是否调用了 super()。这是一个例子:

      @CallSuper
      protected void onAfterAttached(Activity activity) {
          if (activity instanceof ActivityMain) {
              mainActivity = (ActivityMain) activity;
          }
      }
      

      在上面的示例中,后代类中的任何方法覆盖 onAfterAttached 但不调用 super 都会使 Lint 引发错误。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-06-18
        • 2015-05-13
        • 2011-05-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多