Java 对其类的成员具有保护模式,允许子类访问它们。我在某处读到这有问题。我能想到的唯一问题是程序员可能会忘记受保护的成员是 API 的一部分,不能随意更改。还有其他问题吗?
2 回答
首先,我没有看到任何类的受保护成员都可以被视为公共 API 的一部分,因为protected
字段或函数对世界不可见。
最终,在编写 API 时,想法是让接口或合约尽可能保持不变;每次修订都会改变它的完成方式(尽管希望不会太多)。
我可能会在类中看到受保护成员的一个问题是覆盖它们的行为以产生与父级完全不同的行为,同时满足 API 的通用合同(也就是说,如果我期望一个数值,我会得到它; 如果我期待一个布尔值,我会得到它。但是,值/布尔值的正确性是值得怀疑的,应该进行测试)。
示例: 假设我有一个 classParent
和一个 class Child
。两者都实现了一个名为Teachable
. 这使他们能够学习特定的Subject
,这是一个简单的枚举结构。合同Teachable
如下:
public interface Teachable {
/**
* Learn a particular subject.
* This requires that the element being learned hasn't previously been learned.
* @param thisSubject the subject to be learned
* @return whether or not the subject learned was unique.
*/
public boolean hasLearned(Subject thisSubject);
}
假设现在我们实现了Parent
和Child
类,如下所示:
public class Parent implements Teachable {
private Set<Subject> subjectsLearned = new HashSet<>();
@Override
public boolean hasLearned(final Subject thisSubject) {
return learn(thisSubject);
}
protected boolean learn(final Subject theSubject) {
return subjectsLearned.add(theSubject);
}
}
class Child extends Parent {
private Set<Subject> subjectsLearned = new HashSet<>();
@Override
public boolean hasLearned(final Subject thisSubject) {
return learn(thisSubject);
}
@Override
protected boolean learn(final Subject theSubject) {
return !subjectsLearned.add(theSubject);
}
}
我已经有效地改变了类的行为,据说它遵守我的 API 的行为,因为我可以覆盖它。第一次Child
学习一个特定的Subject
,他们会声称他们已经知道它。只有第二次和以后的时间他们不会。
在四个成员访问级别中,私有和默认(包私有)与封装一致,即隐藏的成员保留了实现细节的自主权。换句话说,您的 API 用户对您的类实现细节了解得越少,您与该实现的联系就越少,并且您可以更自由地随时更改它,而不会破坏您的 API 用户所依赖的合同。
一旦您进入受保护的访问(和公共访问),那么您就会被绑定到该实现并且必须继续支持它,直到您的 API 生命周期结束(或者您倒闭)。可访问成员倾向于将您与特定实现联系起来,即使您想更改它,所以......许多可访问成员与紧密封装不一致。
但更重要的是,准入也出口风险。API 导出的类的方面越多,恶意用户可以尝试和破解的潜在漏洞就越多。
例如,受保护的访问可用于覆盖您忘记设置为 final 的类中的方法。这可能会破坏您的 API,更糟糕的是,使恶意用户能够访问您不打算允许的实例数据。