什么时候不要在覆盖时调用super()方法?

问题描述 投票:109回答:7

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

我认为就是这样,从一开始Android团队建议我们总是在每个方法覆盖上调用super

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

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。不要绑定到这个特定的例子。这只是从许多例子中随机挑选的一个例子。

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

java android override
7个回答
140
投票

通过调用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()的调用将按照这个特定的顺序执行save()AB逻辑。如果你没有在super.save()中调用B.save(),那么A.save()就不会被调用。如果你在super.save()之后调用save(b),那么A.save()将会有效地执行B.save()

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

在你提供的SAXParser示例中,这些方法的implementations DefaultHandler只是空的,因此子类可以覆盖它们并为这些方法提供行为。在这个方法的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))只能在方法的最开头调用。


16
投票

他们怎么知道什么时候你必须打电话给超级,什么时候你可以省略它?

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

如果API文档中没有明确说明,那么消费者开发人员可以认为在覆盖它时不必强制调用API方法。由消费者开发人员决定是使用默认行为(调用super方法)还是完全覆盖它。

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


7
投票

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

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

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


3
投票

那么哈维给出了一个更好的答案..但是你可能知道super()在被覆盖的方法中调用时会做什么...它广告你用默认行为做了什么..

e.g:

onDraw() 

在重写时视图类中的方法..在绘制一个视图完全绘制之后,你会在说super.onDraw()之前绘制一些东西..所以这里调用super是必要的,因为android有一些非常重要的事情要做(比如onCreate())

但与此同时

onLongClick()

当你覆盖它时你不想调用super,因为它会显示一个对话框,其中包含EditText或任何其他类似视图的选项列表。这就是基本的差异..你可以选择留下一些...但是对于其他方法,如onCreate() , onStop()你应该让操作系统处理它..


2
投票

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

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

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


2
投票

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

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. 您希望通过多态性添加特定行为的特异性,例如在移动语义的常见动物王国示例中,鸟类移动(飞行)和青蛙移动(跳跃)的方式特定于每个子类。

2
投票

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

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.