使用来自子 class 和抽象父的受保护成员变量

The use of protected member variables from child class and abstract parent

根据我们教授的说法,应不惜一切代价避免使用受保护的可见性。但是,我对为什么有点困惑。假设我们有这个例子:

public abstract class Animal {

    private int maxSpeed;

    public Animal() {} 

    public abstract void setMaxSpeed();

}

其中每个 Animal 都有一个最大速度,需要稍后在子 class 中定义。但是,将其放入:

public class Tutrle extends Animal {

    public Tutrle() {

    }

    @Override
    public void setMaxSpeed() {


    }

}

无法从重写的 setMaxSpeed() 方法中访问 maxSpeed 变量。尽管解决方案是通过 Animal class 的构造函数设置 maxSpeed,但将 maxSpeed 变量设置为 protected 并拥有所有子 classes 都可以访问,稍后定义?

这取决于要求,如果您希望 maxSpeed 变量应该出现在所有子 classes 中,然后将该变量放入 super class 中,然后在中重用该变量子class。为此,您必须通过 subclass 构造函数初始化该变量,并将修饰符更改为该变量的 protected.

但是最好在你的子class中创建变量,如果那些只与你的子class相关,

因为 maxSpeed 成员是在 Animal class 中定义的,所以 class 有一个非抽象的方法来设置它更有意义:

public void setMaxSpeed(int maxSpeed)
{
    this.maxSpeed = maxSpeed;
}

子class类(如Turtle)可以覆盖此方法添加逻辑,但应调用基础class设置值。

@Override
public void setMaxSpeed(int maxSpeed)
{
    if (maxSpeed > 5)
        throw new SomeException();

    super.setMaxSpeed(maxSpeed);
}

如果 setMaxSpeed() 保持抽象,则实现此方法的每个子 class 拥有自己的 maxSpeed 成员会更有意义。

为了从子class访问maxSpeed属性,您可以:

  1. 声明为protected(你的教授似乎不太喜欢这个,但我认为他缺乏合适的解释)
  2. 在 superclass 中声明一个 getMaxSpeed() 方法:如果需要从层次结构外部知道最大速度,请将其声明为 public;否则,将其声明为protected,以便子classes(特定动物,例如您的Turtle)可以知道它们的最大速度是多少。

我同意@Eran 的观点,setMaxSpeed() 方法不应在 Animal superclass 和 sub[=54= 中声明为 abstract ]es 可以从他们自己的 setMaxSpeed 方法中调用 super.setMaxSpeed() 如果他们需要在设置最大速度时进行特定处理。

关于为什么使用protected被声称是'avoided at all costs',或者危险等,请参考这个amazing newsletter's article。我个人认为,这样的说法是错误的,至少是反应过度了。但是,正如文章中所解释的那样:

We should try to only call either private or final methods from inside our constructors. The reason is that Java always calls the most derived method, which means we could call a method on a half-initialized object.

这意味着如果您从 superclass 的构造函数中调用 protected 方法,并且如果 protected 方法在其中一个子 classes,那么该方法中的代码将 运行 的其余部分完全初始化之前,这可能会导致严重的错误:

class Animal {

    protected int maxSpeed;

    protected SomeClass someClass;

    protected Animal(int maxSpeed, SomeClass someClass) {
        this.setMaxSpeed(maxSpeed); // call to subclass method
        this.someClass = someClass;
    }

    public abstract void setMaxSpeed(int maxSpeed); // could also be protected

}

class Turtle extends Animal {

    @Override
    public void setMaxSpeed(int maxSpeed) {
        if (this.someClass.checkIfMaxSpeedMustBeDoubled()) { // throws NPE
            this.maxSpeed = maxSpeed * 2;
        } else {
            this.maxSpeed = maxSpeed;
        }
    }

}

在这个非常简单的示例中,this.someClass.checkIfMaxSpeedMustBeDoubled() 抛出一个 NullPointerException,因为 this.someClass 尚未在 Animal superclass 中初始化。这种错误在使用 protected 成员时很常见,但声称应该避免 protected 是荒谬的。请小心,仅从 superclass' 构造函数中调用 privatefinal 方法,这样就没问题了。