为什么不推荐使用super调用除被覆盖方法以外的其他方法?

4
在Oracle教程中,它说: 如果您的方法覆盖了其超类的方法,则可以通过使用关键字super调用被覆盖的方法。
它提到了在重写方法中使用super的用法。
但是,在实际编写的样例程序中,我可以使用super关键字来访问超类中任何方法。
问题在于:为什么大多数网上讨论super的人总是谈论调用被覆盖的方法?
我的意思是,为什么不建议使用"使用super调用超类中的其他方法"呢?
另外一个问题: 我们不能在静态方法中使用super。编译器不允许我们这样做。
这是因为变量"super"属于对象而不是类,就像关键字"this"一样。静态方法属于类,静态方法没有变量"super"。

对我来说,这有点奇怪和令人困惑,可能会导致问题。我想不出具体的原因,说明它是不良实践。我建议的是,在你需要它的时候要注意,并解决确切的设计问题 - 或者将该设计问题发布到SO,解释Oracle文档反对这种做法并问为什么在您的情况下是不好的。 - djechlin
1
它表明仍需要旧的方法,如果是这种情况,您可能不应该首先覆盖它(在子对象中,旧方法仍然“有意义”)。两个相同名称的方法在同一对象中使用,但具有不同的效果听起来像是一场噩梦。 - Richard Tingle
@RichardTingle 这意味着超类的方法与子类的方法有所不同,两者都是必需的。因此它们应该有不同的名称。我认为这是迄今为止唯一好的答案。您介意发布吗? - djechlin
@djechlin完成:https://dev59.com/coDaa4cB1Zd3GeqP_A2a#23878740 - Richard Tingle
堆栈溢出(Stack Overflow)的问题最好每个帖子只提一个问题。但是我在我的答案中尝试编辑了对您第二个问题的回答。 - Richard Tingle
5个回答

6

为什么在方法foo()之外使用super.foo()是不好的

这表明旧方法仍然需要(并且与子类版本的方法有所不同),在这种情况下,您应该首先考虑不要覆盖它。父方法在子对象中仍然"有意义"。

在同一对象中使用两个名称相同但效果不同的方法听起来像是可读性的噩梦。鉴于两者都在使用,它们似乎应该是具有不同方法名称的单独方法,以清楚地显示它们的细微差别。

为什么使用super.foo()来引用未被覆盖的方法是不好的

这在语义上是一个非常奇怪的事情。父类(非私有)方法是子类的方法,除非被覆盖,使用super.method()method()有相同的效果,因此如果您后来确实重写了方法,则很可能会导致错误,这并不令人惊讶。

与此类似,您可以在接口方法前面加上public,但它没有任何影响。

为什么不能在静态方法中使用super

静态方法无法被覆盖,如果父类和子类恰好具有相同的方法名称,则编译器认为这只是一种巧合(尽管它会隐藏父类中的静态方法 - 而不是覆盖它)。鉴于静态方法无法使用super关键字访问它变得没有意义。此外,您可以像下面这样访问Foo中的静态方法

Foo.staticMethod();


在这个覆盖超类的例子中,它封装了旧的行为并且不通过类的其余部分暴露出来,这与推荐的做法是一致的。 - djechlin
静态方法无法被覆盖。对的。如果,我是说,如果关键字super只能在覆盖的方法中使用,那么在静态方法中使用super就毫无意义了。但是,我们可以使用super来调用超类中的任何方法(我认为这应该是一个不好的想法,这就是为什么我在这里问的原因)。这意味着在覆盖的方法中不强制使用super。这削弱了“鉴于静态方法不能使用super关键字进行覆盖,使用它来访问一个变得毫无意义”的说法。我英语不太好,希望你能理解我的意思。 - Fan Zhang
@FanZhang 啊,我现在明白你的意思了,我已经在我的答案中添加了一个部分来尝试解决这个问题。可以说,在另一个类中访问静态方法的语法是 Foo.staticMethod();。这与非静态方法不同,非静态方法中特定的继承链很重要,因此允许使用 super 会更不清晰。 - Richard Tingle
@RichardTingle 感谢您的详细解释。这里还有一个问题:假设类A有一个静态方法SMA(),类B扩展了类A并且有另一个静态方法SMB()。SMB()只是在类B中定义的静态类,而不是从A继承的。显然,我可以在方法SMB()中使用A.SMA()。但是,有什么阻止我在方法SMB()中使用super.SMA()呢? - Fan Zhang
1
最终,这只是Java语言设计者所做的设计决策。但对我来说,super.SMA()不可用是有道理的,因为这是在另一个类中调用静态方法的更迂回的方式。类层次结构对类的静态部分远不如重要;缺少super引用静态方法进一步强调了这一点(实际上,我只能想到1种非常微小的方式,即子类完全关心其父类的静态部分)。 - Richard Tingle

2
然而在实际情况中,我写的示例程序中,我可以使用super关键字来访问超类中的任何方法。
在真实情况下,除非在特定情况下,否则您不能这样做。您不能使用super调用被访问规则禁止的方法:
- 调用私有方法是被禁止的,除非该类嵌套在其他类中或反之。 - 调用包私有方法是被禁止的,除非该类与其他类在同一包中。 - 调用受保护的方法是允许的。
如果您有一个似乎与此相矛盾的示例,请将其添加到您的问题中,以便我们可以解释实际发生了什么。
这里的问题是:为什么大多数人在线讨论使用super时,他们总是谈论覆盖方法的调用?
我认为您可能正在谈论这个:
public class A {
    public void foo() {...}
}

public class B extends A {
    public void foo() {   // The overriding method.
        ...
        super.foo();      // invoking the overridden method.
        ...
    }
}

子类中的方法调用了自己被覆盖的版本。你会在很多示例中看到它,因为它是最常见的使用情况。就这样。

另一方面,如果方法A.foo()根本没有被覆盖,那么在B中使用super.foo()来调用它是合法的...但我认为这不是好的风格。首先,如果您随后在B中添加一个覆盖方法,它可能会破坏代码。


1
我明白我不能直接调用“祖先”。然而,这不是我的问题。谢谢。 - Fan Zhang
1
@FanZhang - 可以说这就是你的问题。问题在于你的提问语法。你说“... in superclass”,这可能意味着“...在THE superclass中”或“...在A superclass中”。后一种含义将指任何超类。你需要学习英语中介词的用法。 - Stephen C
@StephenC 哦,谢谢。我是指“超类”。 :) - Fan Zhang
@djechlin - 我最初没有看到歧义。我只是以另一种方式阅读了它。我仍然认为另一种阅读方式是有效和有意义的。这就是歧义的问题所在。但因为某人选择了两个可能的含义中的错误含义而责备他们是不合理的。 - Stephen C
@djechlin - 你说的“参考文档”是什么?OP没有提到任何文档。而且它们与问题的歧义无关。 - Stephen C
显示剩余3条评论

1

super.f()绕过了this.f()。如果没有实现this.f(),则不需要super。否则,在this.f()内部未调用时会有一些代码味道。

简而言之:无法找到许多合理的用途来调用super.f()super.f()对任何已知问题都没有解决方案。

我只能想到以下情况:通过使用递归水平列出的框来布局矩形,每个框内部列出垂直列表的框,内部列出水平列表的框等。

public class SpecialBoxLayout extends BoxLayout {

    @Override
    public void layoutHor(...) {
        ... // Something extra, special
        for
            super.layoutVert(...);
        ...
    }

    @Override
    public void layoutVert(...) {
        ...
        for
            super.layoutHor(...);
        ...
    }

这需要继承,使用一个获取额外功能的方法,并且具备互补的方法。这是一种不真正需要的语言特性。就像不存在的super.super.f()一样。

它(调用super.f)是不好的风格,因为:

  • 它跳过了一层继承
  • 非常少见的用法
  • 上述罕见用法可以很好地重写为单参数化的layout(Orientation.VERT, ...)

0
假设你有类A和B,其中B继承自A。类A有方法:methodOne()和methodTwo(),而类B重写了methodOne()。但是要记住,类B也有methodTwo(),因为它是继承来的,所以没有必要通过super来调用它,因为你可以直接在子类中使用它。
编辑: 让我们看一下这个例子:
import java.util.*;
class A {
    public void methodOne() {
        System.out.println("A.1");
        methodTwo();
        //super.methodTwo(); 
            /*This above would lead to compilation error even though
            it would work as intended - always print A.2. 
            Use instead for instance: ((A)this).methodTwo();*/

    }

    public void methodTwo() {
        System.out.println("A.2");
    }
}

class B extends A{
    @Override
    public void methodTwo() {
        System.out.println("B.2");
    }
    public B() {
        methodOne();
    }
}

public class Test {
    public static void main(String[] args) {
        A a = new A();
        B b = new B();
        a.methodTwo();
        b.methodTwo();
    }
}

如果methodOne调用methodTwo,它应该调用methodTwo还是super.methodTwo?如果只需要super.methodTwo呢? - djechlin
@djechlin 我认为TeoTN已经表达了这个观点。如果我从父类继承了methodTwo()方法,那么我不需要使用super。使用super.methodTwo和使用methodTwo是一样的。 - Fan Zhang
@FanZhang 如果methodTwo覆盖了其父类,那它们就不一样了,这也是我假设你问题的意思。 - djechlin
但是methodOne不应该调用super.methodTwo,因为它不适用于A类本身->调用A.methodOne将在Object类中寻找super.methodTwo。在这种情况下,您可以使用强制类型转换。 - TeoTN
@TeoTN 对我来说这毫无意义。是的,你必须避免编写不能编译的代码。在你给出的例子中,从methodOne调用super.methodTwo确实是可以编译的。 - djechlin
显示剩余2条评论

0

你可以使用 super.method() 调用任何方法,但是你只能使用 super() 调用被覆盖的方法。


我知道~但我的问题是:如果我调用除了被覆盖的方法之外的其他方法,会引起一些问题吗?比如使程序更难维护? - Fan Zhang
嗯,我不确定它会调用超类中的方法还是如果你重写了调用的方法它会调用你的方法... - Anubian Noob
2
我非常确定super()调用父类的无参构造函数,但在方法中这是无效的,因为构造函数不是方法。 - Brandon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接