17

我的问题具体涉及 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% 的时间进行扩展。

谢谢!对书籍的特定标题/页码引用比对“..任何基本 Java 编程文本...”的引用更有帮助

============================================ 10-13-2010
这既是关于抽象类的问题,也是关于受保护数据的问题。令人失望的是,焦点似乎已经转移到对数据隐藏在 OOP 中是否是一件好事的回应上(答案:是的)。这里有很多深度涉及抽象类的性质,它与常规的非最终类有何不同,以及在抽象父类中固定数据项的名称和类型以供使用可能有什么优势孩子们的班级。我认为这里有可能将创新和更大的控制从抽象父类扩展到实现子类。我担心一般原则,例如数据隐藏的优势,会成为教条,抑制创新以及新模式和新思想的发展。

感谢所有做出贡献的人。

4

8 回答 8

11

如果字段是私有的并且通过 getter 和 setter 访问,您将能够重新实现 getter 和 setter(例如,删除字段并从外部源更新/读取值),从而改变“字段”的工作方式不接触任何子类。

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

于 2010-08-19T20:09:31.153 回答
9

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

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

于 2010-08-19T20:12:05.000 回答
3

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

于 2010-08-19T23:45:13.447 回答
1

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

使用受保护的并不是不利的一面。但如果没有必要,最好避免它并控制对您的字段的访问。

于 2010-08-19T20:12:05.647 回答
1

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

还有一点,亚历克斯早先提出的。

于 2010-08-19T20:16:09.767 回答
0

信息隐藏很有价值,即使在通过继承相关的类中也是如此。

除了允许重新实现,如上面的 alex 所述:

  • 您可以在方法中设置断点。
  • 您可以在一处添加约束。
于 2010-08-19T20:32:51.423 回答
0

如果有人将您的类子类化,并将子类与您当前的类放在同一个包中,他们可能想要覆盖您的 getter 和 setter。例如,他们想确保i只能设置为大于 1 的值。

除此之外,这真的取决于你。惯例是所有东西都有getter和setter。

于 2010-08-19T20:14:53.420 回答
0

您想使用 getter/setter,因为 usingprotected 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(b.i);//3
        System.out.println(bAsA.i);//2
        System.out.println(bAsBase.i);//1

        b.i = 4;
        bAsA.i = 5;
        bAsBase.i = 6;

        System.out.println(b.i);//4
        System.out.println(bAsA.i);//5
        System.out.println(bAsBase.i);//6
    }
}

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

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

就我个人而言,我对语言设计者完全允许字段覆盖感到相当惊讶。使用 setter 的优点是每个级别都可以保证其自身状态的完整性,并相信派生类没有破坏它。字段覆盖只是自找麻烦。

于 2012-04-08T01:44:43.217 回答