抽象类中受保护的数据

我的问题涉及专门的Java,抽象类和受保护数据的使用。 我被告知所有数据都应该是私有的,并且仅使用受保护的getter / setter。

现在,我理解我们希望屏蔽数据,使其免受该类临时用户的直接操作,并且公共数据成员通常是一个值得怀疑的做法。 我看过“Java protected fields vs public getters”( Java protected fields vs public getters ),但我仍然怀疑:

protected int i; 

在抽象类中比在以下情况更糟糕:

 private int i; protected int geti(); protected void seti(int j); 

当抽象类准确地为子类提供父/公共设施时,我只是没有看到缺点,受保护的范围旨在提供对子项的访问,同时保护数据免受临时用户的影响。 我在上面提到的问题中指出,大多数答案似乎都解决了为什么数据一般应该是私有而不是公开的问题。 我试图将我的问题专门集中在一个抽象的父母中存在的数据,供孩子们使用。 我迄今听到的唯一合理的评论是,使用父母受保护的数据(例如,上面的int i)会在子类中留下引用未在子类中声明的变量的代码。 不太引人注目的是参数(参见基类中的公共受保护数据成员? ),您可能希望某天更改访问权限,现在您必须尊重您的界面。 这是一个抽象类,旨在100%扩展。

谢谢! 特定标题/页面#对书籍的引用对于引用“..anyany basic Java programming text …”更有帮助。

========================================== 10-13-2010
这是关于抽象类的问题,因为它与受保护的数据有关。 我觉得令人失望的是,在OOP中数据隐藏是否是一件好事的回答似乎已经转移(答案:是)。 这里有很多深度涉及抽象类的性质,以及它与常规非final类的区别,以及在抽象父类中修复数据项的名称和类型可能有哪些可能的优点供儿童class。 我认为这里有可能将创新和更大的控制从抽象父类扩展到实现子类。 我担心一般原则,例如数据隐藏的优点,可能成为教条,并抑制创新和新模式和想法的发展。

感谢所有贡献者。

如果该字段是私有的并且通过getter和setter进行访问,则您将能够重新实现getter和setter(例如,删除字段并从外部源更新/读取值),从而更改“字段”的工作方式没有触及任何儿童class。

这是否值得,这取决于你。

将受保护的方法视为子类的接口 ,就像公共方法是其他所有人的接口一样。

提供访问器使基类能够维持其状态:如果没有故意的技巧,子类就不会破坏它。

访问较少并不是一个缺点,这是一个好处。 类应始终限制对尽可能多的内部状态的访问。 不要想到为什么应该隐藏内部,而是考虑为什么应该暴露它们。 在这种情况下,就像在每种情况下一样,除非有充分的理由公开变量,否则不要暴露它。

如果您不需要您的孩子直接访问它,您为什么要让它们?

使用受保护并不是一个缺点。 但如果没有必要,也许最好避免它并控制你的字段访问。

在Java中,除了任何扩展类之外,同一个包中的所有成员都可以访问受保护的成员。 将该字段设为私有将阻止同一个包中的类直接访问它。

同样,亚历克斯早先提出了这一点。

如果有人为您的类创建子类,并将子类放在与当前类相同的包中,则他们可能希望覆盖您的getter和setter。 例如,他们希望确保i只能设置为大于1的值。

除此之外,它真的取决于你。 惯例是,尽管有一切都是吸气剂和制定者。

信息隐藏很有价值,即使在与inheritance相关的类中也是如此。

除了允许重新实施,如上面的亚历克斯所述:

  • 您可以在方法中设置断点。
  • 您可以在一个地方添加约束。

你想使用getter / setter,因为使用protected int i; 允许字段覆盖(您希望不惜一切代价避免)。

您希望禁止字段覆盖,因为它的工作方式与方法覆盖不同。 字段覆盖不会使重写字段无法访问(引用的类型决定了您正在使用的字段的哪个实例)。

可访问的字段应该是最终的,或者是最终的类。

 public class OverridingFun { public static class Base { public int i = 1; public int getI(){ return i; } } public static class A extends Base { public int i = 2; public int getI(){ return i; } } public static class B extends A { public int i = 3; public int getI(){ return i; } } public static void main(String [] args){ B b = new B(); A bAsA = b; Base bAsBase = b; System.out.println(b.getI());//3 System.out.println(bAsA.getI());//3 System.out.println(bAsBase.getI());//3 System.out.println(bi);//3 System.out.println(bAsA.i);//2 System.out.println(bAsBase.i);//1 bi = 4; bAsA.i = 5; bAsBase.i = 6; System.out.println(bi);//4 System.out.println(bAsA.i);//5 System.out.println(bAsBase.i);//6 } } 

乍一看,这看起来像只会使代码难以阅读,但它对function有影响。 假设字段确实被派生类覆盖,因为没有使用setter,没有办法自动更新基本字段,也无法检测是否有人更改了基本字段(因为基值仍可访问)和更新派生字段。 很容易想象基础和派生状态可能会失去同步,并且错误很难被追踪。 简单地说它就是一个非常脆弱的API。

不幸的是,没有办法防范这种情况,因为final关键字可以防止覆盖,也会使字段写入一次。 所以没有可写的不可重载的字段。

就个人而言,我很惊讶语言设计师允许字段覆盖。 使用setter的优点是每个级别都可以保证它自己的状态的完整性,并相信派生类没有对它进行污染。 现场覆盖只是在寻找麻烦。