将“final”修饰符与 getter 和 setter 一起使用是个好主意吗?
Posted
技术标签:
【中文标题】将“final”修饰符与 getter 和 setter 一起使用是个好主意吗?【英文标题】:Is it a good idea to use 'final' modifier with getters and setters? 【发布时间】:2014-10-09 08:40:42 【问题描述】:我想知道为什么最终修饰符不与 getter 和 setter 一起使用?
为什么这样做:
private int x;
public void setX(int x)
if(x >= 1) throw new IllegalArgumentException("X must be lower than 1");
this.x = x;
而不是这个:
private int x;
public final void setX(int x)
if(x >= 1) throw new IllegalArgumentException("X must be lower than 1");
this.x = x;
它没有提高封装性?我一直试图用谷歌澄清它,但我没有运气。
提前致谢。
【问题讨论】:
这一切都取决于。你打算制作 POJO 吗? 最好的封装是避免getter和setter(通常是可能的),并尝试遵循Tell,Don't ask的哲学。 this(“Java 最终修饰符”问题@***)是否有助于回答您的问题? 如果 POJO 的目标是不可变,那么创建一个 getterfinal
是有意义的。
final
修饰符将禁用子类以覆盖该方法。如果你想要这个设计,你应该只使用它,但这取决于。没有具体的答案。此外,对于代码优化,请将其留给 JIT,除非通过分析器证明该代码实际上会表现得更好。
【参考方案1】:
您可能希望将 setter 保留为非 final 的一个原因是让子类进行自己的更严格的参数检查:
public class Subclass extends Superclass
public void setX(int x)
if(x >= 0) throw new IllegalArgumentException("X must be negative");
super.setX(x);
当然这会破坏Liskov Substitution Principle,因为子类加强了子类型中的先决条件。
【讨论】:
而主要的原因是你根本不知道派生类是否想要这样做,所以除非你有非常好的理由 不要让你的二传手final
。 non-final 应该是默认值。
另一方面,这也允许子类做不那么严格的检查,这可能会干扰超类的一些逻辑,所以这个参数可以双向使用。
@Eran 不,它不会让子类削弱检查:如果您调用 super.setX(x);
的值未通过基数检查,您最终会得到一个异常(反射可以让你通过检查,但是反射可以让你做很多坏事,所以我在这里不谈论它)。您可以为 x
提供自己的存储空间,因为基础中的 int x
是私有的,但这通常是一件非常糟糕的事情。
这是一个可能破坏 Liskov 替换原则的好方法......(专业)开发人员不应该这样做。
请注意,如果您将 getter 标记为 final
,则覆盖 setter 不会破坏超类的不变量,除了在 getX()
之后调用的 getX()
应该返回新的价值。不过,它不会使班级处于内部不一致的状态。【参考方案2】:
Java 中 final 方法的目的是它不能被子类覆盖或隐藏。因此,如果您的 getter / setter 需要该功能,将它们设为 final 是完全可以的,否则没有这样做的目的。
【讨论】:
【参考方案3】:当可能存在另一种更强大的可能性时,我会严格避免设置器:
您可能需要提供一个 setter,以防您想改变一个字段。
你提到的问题:
如果开发人员违反了我提供的验证规则,该怎么办? 覆盖 setter...我们应该使用 final 关键字吗?
我会说:“劝他这样做”。
确实,声明 setter 就像对世界说: “你可以给我一些字段值,然后你可以和我一起做你自己的事!” => 程序代码
“告诉!不要问”哲学会说: “客户,告诉我该怎么做,我会做的。” 通常,您的主要“复杂”逻辑将位于 POJO 的主要公共 api 中。
我怀疑开发人员是否会试图覆盖 POJO 的完整逻辑而不会冒完全不良行为的风险..
因此,例如,不要将打开的 setter 声明为任何值,而是强制将值传递给 main 方法,以便您可以控制流程:
public void computeWith(int x)
if(x <= 0) throw new IllegalArgumentException("X must be superior to 0");
//code computing here.
您会注意到,通过这种方式,POJO 中甚至可能不再需要某些字段。
总结:很容易覆盖一个糟糕的验证规则......但对行为有风险。
只要我的 2 美分。
【讨论】:
【参考方案4】:两种方式都有争论,但如果你将方法设置为最终方法(任何类型的,不仅仅是 getter 或 setter),另一个开发人员会在某个地方诅咒你。很难预测您的类在未来将如何使用,并且通过使其成为 final 来限制它通常会使未来的开发人员变得更加困难。
例如,一些可怜的人不得不将您的代码与公司刚刚购买的新应用程序集成。现在是周五下午 3 点,演示将于周一早上到期。如果他们可以覆盖一个 getter,他们可以强制它从新应用程序返回一个包装的对象,并且一切都会正常工作。如果那个 getter 是最终的,他们必须开始改变,测试可能会发布你的代码,所以他们最终会在周末工作。
所以对未来的开发者抱有同情心,除非你真的很确定,否则不要将方法定为最终的。即使那样我也不会打扰。
专门针对非最终 getter 的另一个用途是测试。重写 getter 以注入类的测试实例非常方便,尤其是在代码不完美的集成测试期间。
【讨论】:
以上是关于将“final”修饰符与 getter 和 setter 一起使用是个好主意吗?的主要内容,如果未能解决你的问题,请参考以下文章
kotlin学习之类的修饰符与抽象类,嵌套类,内部类,匿名内部类
初学总结--------Java修饰符与修饰关键字(且叫修饰关键字)