【问题标题】:Use of 'super' keyword when accessing non-overridden superclass methods访问非覆盖的超类方法时使用“super”关键字
【发布时间】:2012-10-09 15:23:07
【问题描述】:

我正在尝试在 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(以在代码中显示您正在访问超类),或者只在被覆盖/隐藏的实例中使用(所以它们代表出去)。

【问题讨论】:

    标签: java coding-style super


    【解决方案1】:

    不要使用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
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      不使用super 关键字访问getTyreCost 是正确的做法。

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

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

      【讨论】:

      • 好地方(这只是我作为问题的一部分写的一个例子)。成员现已更新并设置为私有。
      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          【解决方案5】:

          如果你使用super,你明确告诉使用超类方法(不管子类有没有被覆盖的方法),否则首先jvm检查子类中的方法(如果有的话,被覆盖的方法),如果不可用使用超类方法。

          【讨论】:

            【解决方案6】:

            覆盖意味着在具有相同方法签名的子类中重新定义超类中的方法。在你的情况下,getTyreCost() 方法没有被覆盖,你没有在你的子类中重新定义方法,所以不需要使用super.getTyreCost(),只有getTyreCost() 会做(就像super.getTyreCost() 会做同样的事情)。 super 关键字在方法被覆盖时使用,并且您希望在超类中实现子类中的方法调用。

            【讨论】:

              【解决方案7】:

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

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

              【讨论】:

              • 投反对票?为什么? Downvoter,请注意发表评论。也许我可以学到一些东西。
              • 是我!我觉得“无论你是否使用它,它都没有任何区别”可能会产生误导,尤其是在进一步尝试继承和覆盖的情况下。诚然,你用“在这种情况下”来结束这句话,这使它有点准确——但我仍然认为它可能会产生误导/混淆。稍微重新措辞一下,我很乐意取消投票!
              • @RichardJPLeGuen:我改写了我的答案并删除了误导性部分。如果有任何东西仍然具有误导性,请告诉我,我非常乐意进行编辑。为评论 +1。
              【解决方案8】:

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

              【讨论】:

                猜你喜欢
                • 2012-08-05
                • 1970-01-01
                • 2015-10-07
                • 2012-02-29
                • 1970-01-01
                • 1970-01-01
                • 2011-11-03
                • 2016-11-10
                • 2012-05-01
                相关资源
                最近更新 更多