【问题标题】:When NOT to call super() method when overriding?覆盖时何时不调用 super() 方法?
【发布时间】:2012-05-01 22:53:31
【问题描述】:

当我创建自己的 Android 自定义类时,我 extend 它的原生类。然后当我想重写基方法时,我总是调用super() 方法,就像我在onCreateonStop 等中一样。

我认为就是这样,因为从一开始 Android 团队就建议我们在每次方法覆盖时始终调用 super

但是,在许多书籍中,我可以看到比我更有经验的开发人员经常忽略调用super,我真的怀疑他们这样做是因为缺乏知识。例如,看一下这个基本的SAX 解析器类,其中startElementcharactersendElement 中省略了super

public class SAXParser extends DefaultHandler{
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if(qName.equalsIgnoreCase("XXY")) {
            //do something
        }
    }

    public void characters(char[] ch, int start, int length) throws SAXException {
        //do something
    }

    public void endElement(String uri, String localName, String qName) throws SAXException {
        if(qName.equalsIgnoreCase("XXY")) {
            //do something
        }else () {
            //do something
        }
    }
}

如果您尝试通过 Eclipse 或任何其他 IDE 创建任何覆盖方法,super 将始终作为自动化过程的一部分创建。

这只是一个简单的例子。书籍充满了类似的代码

他们如何知道您何时必须调用 super 以及何时可以省略调用?

PS。不要绑定到这个特定的例子。这只是从许多例子中随机挑选的一个例子。

(这听起来像是一个初学者的问题,但我真的很困惑。)

【问题讨论】:

  • endElement 的 API 文档说“默认情况下,什么都不做。应用程序编写者可能会覆盖这个方法......”这意味着您可以安全地调用 super,因为它“什么都不做”,但您不这样做不必,你可以真正覆盖它。如果您阅读该方法的文档,您通常可以判断您是否必须/可以/不应该这样做。
  • 小例子:我在启动画面中使用 onBackPressed(),并且我不调用 super 所以它不会退出启动,它只是禁用按钮。
  • @sandalone 很抱歉在这里提问,但是您是否有关于如何通过应用内结算处理增值税的经验,以及哪些国家/地区由 Google 自动完成,哪些国家/地区必须手动报告/缴纳增值税?见stackoverflow.com/questions/36506835/…

标签: java android overriding


【解决方案1】:

通过调用super 方法,您并没有覆盖该方法的行为,而是扩展它。

super 的调用将执行您要扩展的类为该方法定义的任何逻辑。考虑到在方法覆盖中调用super 的实现时可能很重要。例如:

public class A { 
    public void save() { 
         // Perform save logic
    }
}

public class B extends A {
    private Object b;
    @Override
    public void save() { 
        super.save(); // Performs the save logic for A
        save(b); // Perform additional save logic
    }
}

B.save() 的调用将按此特定顺序为AB 执行save() 逻辑。如果您没有在B.save() 中调用super.save(),则不会调用A.save()。如果你在save(b) 之后调用super.save()A.save() 将在B.save() 之后有效地执行。

如果你想覆盖 super 的行为(即完全忽略它的实现并自己提供),你不应该调用super

在您提供的SAXParser 示例中,这些方法的DefaultHandlerimplementations 只是空的,因此子类可以覆盖它们并为这些方法提供行为。在此方法的javadoc 中也指出了这一点。

public void startElement (String uri, String localName,
    String qName, Attributes attributes) throws SAXException {
    // no op
}

关于 IDE 生成的代码中的 super() 默认调用,正如 @barsju 在他的评论中指出的那样,在每个构造函数中都有对 super() 的隐式调用(即使您没有在代码中编写它) ,这意味着,在这种情况下,调用super 的默认构造函数。 IDE 只是为您写下来,但如果您删除它,它也会被调用。另请注意,在实现构造函数时,super() 或其任何带参数的变体(即super(x,y,z))只能在方法的最开头调用。

【讨论】:

  • 另外不要注意对于构造函数super()(超类的默认构造函数)总是被调用,即使你没有指定它。如果超类没有默认构造函数,如果你没有显式调用超类的一个构造函数作为子类构造函数中的第一条语句,则会出现编译错误。
  • 是的,这基本上只是懒惰 - 对于我们知道不做任何事情的方法,为了简洁起见就省略它
  • 感谢您宝贵的 cmets,@barjsu,我已将这些详细信息包含在答案中。
【解决方案2】:

他们如何知道何时必须调用 super 以及何时可以省略调用?

通常,如果一个特殊的 API 方法对底层框架上下文生命周期具有关键意义,它总是会在 API 文档中明确说明和突出显示,例如 Activity.onCreate() API documentation。此外,如果 API 遵循稳健的设计,它应该在项目编译时抛出一些异常来提醒消费者开发者,并确保它不会在运行时产生错误。

如果 API 文档中没有明确说明这一点,那么消费者开发人员可以非常安全地假设 API 方法在覆盖它时不是必须调用的。由消费者开发者决定是使用默认行为(调用super 方法)还是完全覆盖它。

如果条件允许(我喜欢开源软件),消费者开发人员可以随时查看 API 源代码,了解该方法实际上是如何编写的。以Activity.onCreate() sourceDefaultHandler.startElement() source 为例。

【讨论】:

  • 感谢您向我解释更多内容。感谢您提醒我查看源代码。
【解决方案3】:

你应该在脑海中做的测试是:

“我是否希望为我完成此方法的所有功能,然后再做一些事情?”如果是,那么你想调用super(),然后完成你的方法。对于诸如onDraw() 之类的“重要”方法也是如此,它在后台处理很多事情。

如果您只想要某些功能(就像您将覆盖的大多数方法一样),那么您可能不想调用super()

【讨论】:

    【解决方案4】:

    Xavi 给出了更好的答案.. 但您可能知道super() 在被覆盖的方法中调用时会做什么...它会宣传您对默认行为所做的事情。 .

    例如:

    onDraw() 
    

    视图类中的方法被覆盖时..你在说 super.onDraw() 之前绘制一些东西,一旦视图完全绘制,它就会出现..所以这里调用super 是必要的,因为android有一些至关重要的事情要做(比如onCreate())

    但同时

    onLongClick()
    

    当您覆盖它时,您不想调用 super,因为它会弹出一个对话框,其中包含 EditText 或任何其他类似视图的选项列表。这是基本的差异。您可以选择离开它一段时间。 . 但是对于像onCreate() , onStop() 这样的其他方法,您应该让操作系统处理它..

    【讨论】:

      【解决方案5】:

      我实现了一个类似的约束数组列表

      public class ConstraintArrayList<T> extends ArrayList<T> {
        ConstraintArrayList(Constraint<T> cons) {this.cons = cons;}
        @Override
        public boolean add(T element) {
          if (cons.accept(element))
            return super.add(element);
          return false;
        }
      }
      

      如果您查看代码,它只是在实际让超类执行将元素实际添加到列表之前进行一些预检查。 这说明了方法覆盖的两个原因之一:

      1. 您想要扩展超类可以做什么的可扩展性
      2. 您希望通过多态性添加特定行为的特定性,例如在常见的动物界移动语义示例中,鸟类移动(飞行)和青蛙移动(跳跃)的方式特定于每个子类。

      【讨论】:

        【解决方案6】:

        我没有清楚地了解你的问题,但如果你问为什么不调用super 方法:

        调用super方法是有原因的:如果父类中没有零参数构造函数,那么就不可能为此创建一个子类,所以要么你需要保留一个无参数构造函数父类,或者您需要在子类构造函数的顶部使用argument(how much argument constructor you have used in super class) 定义super() 调用语句。

        我希望这会有所帮助。如果没有,请告诉我。

        【讨论】:

          【解决方案7】:

          对于那些还想知道 Android 框架中哪些被覆盖的方法应该调用 super 并发现这个问题的人 - 这是 2019 年的当前提示 - Android Studio 3+ 会在您需要时告诉您

          【讨论】:

            猜你喜欢
            • 2022-11-25
            • 2020-09-20
            • 2012-08-09
            • 2023-01-07
            • 2021-04-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-10-24
            相关资源
            最近更新 更多