我很少使用继承,但是当我这样做时,我从不使用受保护的属性,因为我认为它打破了继承类的封装.
你使用受保护的属性吗?你用它们做什么的?
在Bill Venners的设计访谈中,Effective Java的作者Joshua Bloch 说:
信任子类
Bill Venners: 我是否应该比非子类更亲密地信任子类?例如,我是否使子类实现更容易打破我,而不是非子类?特别是,您对受保护数据的看法如何?
Josh Bloch:假设你给子类访问你的内部数据结构,那么编写一个既可子类化又强健对抗恶意子类的东西实际上是一件非常困难的事情.如果子类无法访问普通用户没有访问的任何内容,则子类更难以进行破坏.但除非你将所有方法都设置为final,否则子类仍然可以通过执行错误的操作来响应方法调用来破坏你的契约.这就是为什么像String这样的安全关键类是最终的.否则,有人可能会编写一个子类,使Strings看起来可变,这足以破坏安全性.所以你必须信任你的子类.如果您不信任它们,那么您就不能允许它们,因为子类很容易导致类违反其合同.
就一般受保护的数据而言,这是一种必要的恶魔.它应该保持在最低限度.大多数受保护的数据和受保护的方法相当于提交实现细节.受保护的字段是您对子类可见的实现细节.即使受保护的方法也是一个内部结构,您可以使子类可见.
使其可见的原因在于,为了允许子类完成工作或有效地执行工作,通常需要这样做.但是一旦你完成它,你就会致力于它.即使您后来发现不再涉及使用特定字段或方法的更有效的实现,现在也是您不允许更改的内容.
所以在所有其他条件相同的情况下,你根本不应该有任何受保护的成员.但是,如果你的人数太少,那么你的班级可能无法用作超级班级,或者至少不是一个有效的超级班级.通常你会发现事后.我的理念是,当你第一次上课时,尽可能少的受保护成员.然后尝试将其子类化.您可能会发现,如果没有特定的受保护方法,所有子类都必须做一些坏事.
例如,如果你看一下
AbstractList
,你会发现有一个受保护的方法可以在一个镜头中删除列表的范围(removeRange
).为什么那里?因为基于公共API删除范围的正常习惯是调用subList
获取子List
,然后调用clear
该子List
.但是,如果没有这种特殊的受保护方法,唯一clear
可以做的就是重复删除单个元素.想一想.如果你有一个数组表示,它会做什么?它会反复折叠数组,执行N次N次.因此,它需要一定数量的工作,而不是它应该的线性工作量.通过提供这种受保护的方法,我们允许任何可以有效删除整个范围的实现.任何合理的
List
实现都可以一次性更有效地删除范围.我们需要这种受保护的方法,你需要比我更聪明才能预先知道.基本上,我实现了这个东西.然后,当我们开始对它进行子类化时,我们意识到范围删除是二次的.我们买不起,所以我加入了受保护的方法.我认为这是受保护方法的最佳方法.尽可能少地放入,然后根据需要添加更多.受保护的方法代表您可能想要更改的设计承诺.您始终可以添加受保护的方法,但无法将其取出.
Bill Venners: 受保护的数据?
乔什布洛赫:同样的事情,但更多.在弄乱数据不变量方面,受保护的数据更加危险.如果您让其他人访问某些内部数据,他们可以免费统治它.
简短版本:它打破了封装,但它是一个必须保持最低限度的必要的邪恶.
C#:
我将protected用于抽象或虚拟方法,我希望基类重写.如果它可以被基类调用,我也会使方法受到保护,但我不希望它在类层次结构之外调用.
您可能需要它们用于静态(或"全局")属性,您希望您的子类或来自相同包的类(如果它是关于Java)受益.
表示某种"常量值"的静态最终属性很少有getter函数,因此受保护的静态final属性在这种情况下可能有意义.