Getter Setter:使用还是不使用?

Posted Java攻城师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Getter Setter:使用还是不使用?相关的知识,希望对你有一定的参考价值。

经验丰富的Java开发人员会在何时以及何时不使用代码库中的getter和setter以及accessor方法的情况下进行检查。

Java Getter设置器

为什么我们将实例变量设为私有?我们不希望其他类依赖它们。而且,它需要灵活性,可以一时兴起或一时冲动地更改变量的类型或实现。那么,为什么程序员会自动将getter和setter添加到其对象中,从而像公开一样公开其私有变量?

存取器方法

访问器(也称为getter和setter)是使您可以读写对象的实例变量的值的方法。

public class AccessorExample {
private String attribute;
public String getAttribute() {

return attribute;

}
public void setAttribute(String attribute) {

this.attribute = attribute;

}
}

为什么要使用存取器?

实际上,有很多充分的理由考虑使用访问器,而不是直接公开类的字段。

Getter和Setter使API更加稳定。例如,考虑一个类中的公共字段,其他类可以访问该字段。稍后,如果我们想在获取和设置变量时添加任何额外的逻辑,这将影响使用该API的现有客户端。因此,对此公共字段进行的任何更改都将要求对引用它的每个类进行更改。另一方面,使用访问器方法,可以轻松添加一些逻辑,例如缓存某些数据或延迟初始化。

访问器方法还允许我们在新值与先前值不同的情况下触发属性更改事件。

使用setter设置值的另一个优点是,在设置值时,我们可以使用该方法保留不变式或执行一些特殊处理。

所有这些对于使用accessor方法获取值的类都是无缝的。

我应该为我的所有字段都使用访问器方法吗?

对于包私有或私有嵌套类,可以将字段声明为公共字段。与访问器方法相比,在类定义中以及在使用它的客户端代码中,与访问器方法相比,暴露这些类中的字段所产生的视觉混乱更少。

如果一个类是程序包私有的或私有嵌套的类,则公开其数据字段并没有天生的错误-假设它们在描述该类提供的抽象方面做得足够好。

此类代码仅限于声明该类的包,而客户端代码则绑定到一个类的内部表示。我们可以更改它,而无需修改该程序包之外的任何代码。而且,在私有嵌套类的情况下,更改的范围进一步限于封闭类。

使用公共字段的设计的另一个示例是JavaSpace条目对象。肯·阿诺德(Ken Arnold)描述了他们决定使用此处的get和set方法将这些字段公开而不是私有的过程。

现在,这有时使人们感到不舒服,因为有人告诉他们不要使用公共场所,并且公共场所不好。规则有其原因。专用数据规则的原因不适用于这种特殊情况。这是该规则的罕见例外。我还告诉人们不要在其对象中放置公共字段,但是存在例外。这是该规则的一个例外,因为仅说它是一个字段就更简单,更安全。我们坐下来问:为什么是这样的规则?是否适用?在这种情况下,不是。

专用字段+公共访问器==封装

考虑下面的示例:

public class A {
public int a;
}

通常,这被认为是不良的编码实践,因为它违反了封装。另一种方法是:

public class A {
private int a;
public void setA(int a) {

this.a =a;

}
public int getA() {

return this.a;

}
}

有人认为这封装了属性。现在,这真的是封装吗?

事实是,getter / setter与封装无关。在这里,数据没有比在公共领域中更多地被隐藏或封装。其他对象仍然对类的内部知识有深入的了解。对类所做的更改可能会泛滥,并在从属类中强制执行更改。以这种方式,getter和setter通常会破坏封装。真正封装良好的类没有设置方法,最好也没有设置方法。该类应该负责使用其数据计算某些内容,然后返回结果,而不是向类询问一些数据然后使用它来计算某些内容。

考虑下面的示例:

public class Screens {
private Map screens = new HashMap();
public Map getScreens() {

return screens;

}
public void setScreens(Map screens) {

this.screens = screens;

}
// remaining code here

}

如果需要获取特定屏幕,请编写如下代码:

Screen s = (Screen)screens.get(screenId);

这里有一些值得注意的事情…

客户端需要从Map中获取一个Object并将其强制转换为正确的类型。而且,最糟糕的是,地图的任何客户端都有权将其清除,而这通常不是我们通常想要的。

相同逻辑的另一种实现是:

public class Screens {
private Map screens = new HashMap();
public Screen getById(String id) {

return (Screen) screens.get(id);

}
// remaining code here
}

在此,Map实例和边界处的接口(Map)被隐藏。

吸气剂和二传手被过度使用

创建私有字段,然后使用IDE为所有这些字段自动生成getter和setter几乎和使用public字段一样糟糕。

过度使用的原因之一是,在IDE中,只需单击几下即可创建这些访问器。完全无意义的getter / setter代码有时比类中的真实逻辑还要长,即使您不想这样做,您也会多次阅读这些函数。

所有字段都应保持私有状态,但是,设置器仅应在有意义的情况下保持私有状态,从而使该对象不可变。添加不必要的吸气剂会揭示内部结构,这是增加耦合的机会。为了避免这种情况,每次添加访问器之前,我们都应该分析是否可以封装行为。

让我们再举一个例子:

public class Money {
private double amount;
public double getAmount() {

return amount;

}
public void setAmount(double amount) {

this.amount = amount;

}
//client
Money pocketMoney = new Money();
pocketMoney.setAmount(15d);
double amount = pocketMoney.getAmount(); // we know its double
pocketMoney.setAmount(amount + 10d);
}

根据上述逻辑,以后,如果我们假设double不是要使用的正确类型,而应该使用BigDecimal,那么使用该类的现有客户端也会中断。

让我们重组上面的示例:

public class Money {
private BigDecimal amount;
public Money(String amount) {

this.amount = new BigDecimal(amount);

}
public void add(Money toAdd) {

amount = amount.add(toAdd.amount);

}
// client
Money balance1 = new Money("10.0");
Money balance2 = new Money("6.0");
balance1.add(balance2);
}

现在,班级不再要求价值,而是有责任增加自己的价值。使用这种方法,将来对任何其他数据类型的更改请求都不需要更改客户端代码。

结论

与使用公共字段相比,使用访问器来限制对字段变量的直接访问更为可取,但是,使每个字段都使用getter和setter会显得过大。不过,这也取决于情况。有时您只想要一个哑数据对象。访问器应添加到真正需要它们的字段中。一个类应该暴露出恰好使用其状态的更大行为,而不是暴露由其他类操纵的状态存储库。

参考: 《2020最新Java基础精讲视频教程和学习路线!》
链接:https://blog.csdn.net/weixin_...

以上是关于Getter Setter:使用还是不使用?的主要内容,如果未能解决你的问题,请参考以下文章

Lombok 写一个@Data更好还是@Getter+@Setter更好?

c# 使用基类中的 setter 或 getter

在 Lombok 中省略一个 Setter/Getter

Python使用@property装饰器--getter和setter方法变成属性

OOP Javascript - Getter & Setter

龙目岛不生成getter和setter?