抽象类中受保护的数据

时间:2010-08-19 20:06:09

标签: java abstract protected

我的问题涉及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)会在子类中留下引用未在子类中声明的变量的代码。不太引人注目的是你可能想要在某一天更改访问权限的论点(参见Common protected data member in base class?),现在你必须尊重你的界面。这是一个抽象类,旨在100%扩展。

谢谢!特定标题/页面#书籍参考对于“..any basic Java programming text ...”

的引用更有帮助

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

感谢所有贡献者。

8 个答案:

答案 0 :(得分:11)

如果该字段是私有的并且通过getter和setter进行访问,那么您将能够重新实现getter和setter(例如,删除字段并从外部源更新/读取值),从而改变“ “无需接触任何儿童课程即可工作。

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

答案 1 :(得分:9)

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

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

答案 2 :(得分:3)

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

答案 3 :(得分:1)

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

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

答案 4 :(得分:1)

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

同样,亚历克斯早些时候提出了这一点。

答案 5 :(得分:0)

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

除此之外,这取决于你。惯例是,尽管如此,仍有吸气剂和固定剂。

答案 6 :(得分:0)

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

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

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

答案 7 :(得分:0)

你想使用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(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
    }
}

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

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

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