8

我试图在 Java 中掌握继承的窍门,并了解到当覆盖子类中的方法(和隐藏字段)时,仍然可以使用“super”关键字从超类访问它们。

我想知道的是,'super' 关键字是否应该用于非覆盖方法?

有什么区别(对于非覆盖方法/非隐藏字段)?

我在下面整理了一个例子。

public class Vehicle {
    private int tyreCost;

    public Vehicle(int tyreCost) {
         this.tyreCost = tyreCost;
    }

    public int getTyreCost() {
        return tyreCost;
    }        
}

public class Car extends Vehicle {
    private int wheelCount;

    public Vehicle(int tyreCost, int wheelCount) {
        super(tyreCost);
        this.wheelCount = wheelCount;
    }   

    public int getTotalTyreReplacementCost() {
        return getTyreCost() * wheelCount;
    }   
}

具体来说,鉴于getTyreCost()尚未被覆盖,应该getTotalTyreReplacementCost()使用getTyreCost(), 还是super.getTyreCost()?

我想知道是否应该在访问超类的字段或方法的所有实例中使用 super(以在代码中显示您正在访问超类),或者只在被覆盖/隐藏的实例中使用(以便它们脱颖而出)。

4

8 回答 8

13

不要使用super关键字来引用其他未被覆盖的方法。它使其他试图扩展您的类的开发人员感到困惑。

让我们看一些确实以这种方式使用super关键字的代码。这里我们有 2 个类:DogCleverDog

/* file Dog.java */
public static class Dog extends Animal {

    private String name;

    public Dog(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

}

/* file CleverDog.java */
public class CleverDog extends Dog {

    public CleverDog(String name) {
         super(name);
    }

    public void rollover() {
        System.out.println(super.getName()+" rolls over!");
    }

    public void speak() {
        System.out.println(super.getName() + " speaks!");
    }

}

现在,假设您是该项目的新开发人员,并且您需要为电视上的一只聪明的狗提供一些特定的行为:这只狗必须完成所有的把戏,但应该使用它虚构的电视名称。为此,您覆盖该getName(...)方法...

/* file DogOnTv.java */
public class DogOnTv extends CleverDog {

    String fictionalName;

    public DogOnTv(String realName, String fictionalName) {
        super(realName);
        fictionalName = fictionalName;
    }

    public String getName() {
        return fictionalName;
    }

}

...并落入由原始开发人员设置的陷阱以及他们对super关键字的不寻常使用!

上面的代码不起作用 - 因为在原始CleverDog实现中,是使用关键字getName()调用的。super这意味着它总是调用Dog.getName()- 与任何覆盖无关。因此,当您使用新DogOnTv类型时...

    System.out.println("Showcasing the Clever Dog!");
    CleverDog showDog = new CleverDog("TugBoat");
    showDog.rollover();
    showDog.speak();

    System.out.println("And now the Dog on TV!");
    DogOnTv dogOnTv = new DogOnTv("Pal", "Lassie");
    dogOnTv.rollover();

...你得到错误的输出:

Showcasing the Clever Dog!
Tugboat rolls over!
Tugboat speaks!

And now the Dog on TV!
Pal rolls over!
Pal speaks!

super当你重写一个方法时,这不是通常的预期行为,所以你应该避免在不属于它的地方使用关键字造成这种混乱。

但是,如果这实际上是您想要的行为,请改用final关键字 - 以清楚地表明该方法不能被覆盖:

/* file CleverDog.java */
public class CleverDog extends Dog {

    public CleverDog(String name) {
         super(name);
    }

    public final String getName() { // final so it can't be overridden
        return super.getName();
    }

    public void rollover() {
        System.out.println(this.getName()+" rolls over!"); // no `super` keyword
    }

    public void speak() {
        System.out.println(this.getName() + " speaks!"); // no `super` keyword
    }

}
于 2012-10-09T16:11:46.397 回答
3

通过不使用super关键字访问getTyreCost.

但是您应该将您的成员设置为私有,并且只使用 getter 方法。

Usingsuper关键字应该保留给需要显式调用父方法的构造函数和重写方法。

于 2012-10-09T15:27:16.743 回答
2

这将取决于您计划如何使用代码。如果您指定super.getTyreCost()然后覆盖该方法。您仍将调用超类上的方法,而不是覆盖的版本。

在我看来,调用 super 可能会在以后导致更多的混乱,所以最好仅在您有明确需要时才指定。但是,对于您在此处介绍的情况 - 行为不会有任何差异。

于 2012-10-09T15:27:53.380 回答
2

这取决于您的需求和愿望。使用super强制编译/应用程序忽略当前类中的任何潜在方法。如果你想传达你只想使用父母的方法,那么使用super是合适的。它还将防止将来对您的类进行修改以意外覆盖父方法,从而破坏您的预期逻辑。

然而,这些情况相当罕见。在课堂上到处使用 super 会导致代码非常混乱和混乱。在大多数一般情况下,仅在您自己的类中调用该方法并允许编译器/jvm 确定需要调用哪个方法(超级或本地)更合适。它还允许您干净地覆盖/修改/操作超级的返回值。

于 2012-10-09T15:30:37.103 回答
1

如果使用super,则明确告诉使用超类方法(无论子类是否具有覆盖方法),否则首先 jvm 检查子类中的方法(如果有则覆盖方法),如果不可用则使用超类方法。

于 2012-10-09T15:26:13.333 回答
0

覆盖意味着从具有相同方法签名的子类中的超类重新定义方法。在您的情况下,该getTyreCost()方法尚未被覆盖,您尚未在子类中重新定义该方法,因此无需使用super.getTyreCost(),只会getTyreCost()做(就像super.getTyreCost()会做同样的方式)。super当方法已被覆盖,并且您希望子类中的方法调用在超类中实现时使用关键字。

于 2012-10-09T15:26:47.517 回答
0

从技术上讲,在这种情况下调用的是继承版本,其实现实际上是由父类提供的。

在某些情况下,您必须使用super关键字。例如,如果Car重写了该方法,以提供其自身的不同实现,但您需要调用父类提供的实现,那么您将使用super关键字。在这种情况下,您不能忽略super关键字,因为如果您这样做了,那么您将调用子类本身提供的实现。

于 2012-10-09T15:32:21.787 回答
-1

建议对继承类的进一步更改不需要添加超级​​限定符,并且如果错过也可以防止错误。

于 2012-10-09T15:25:43.177 回答