为什么在Java中getter和setter方法很重要?

38

我一直被教导要始终使用getter和setter。然而,我不知道这些方法的优缺点,因为通过实现它们,我们既暴露了数据,又隐藏了数据。

我对此有些困惑。有人能够给出关于为什么使用getter/setter以及其优势的适当建议吗?

6个回答

72

基本的“私有字段和公共getter和setter”模式在封装方面确实是完全无意义的,除了它给你一个机会稍后更改而不更改API。

因此,请不要盲目使用该模式。仔细考虑您实际需要哪些操作。

getter和setter的真正重点在于只在适当的情况下使用它们,并且它们可以做更多的事情,而不仅仅是获取和设置字段。

  • 您只能拥有一个getter。然后属性是只读的。实际上,这应该是最常见的情况。
  • 您只能拥有一个setter,使属性可配置,但传达其他任何东西都不应该依赖其值
  • getter可以从几个字段计算一个值而不是返回一个字段。
  • getter可以进行防御性复制
  • getter可以懒惰地执行昂贵的提取操作并使用字段缓存值
  • setter可以进行合理性检查并抛出 IllegalArgumentException
  • setter可以向监听器通知值的更改
  • 您可以拥有一组将多个字段一起设置的setter,因为它们在概念上属于一起。这不符合JavaBeans规范,因此如果您依赖于期望JavaBeans的框架或工具,则不要这样做。否则,它是一个有用的选项。

所有这些都是隐藏在简单的“getter和setter”接口背后的实现细节。这就是封装的含义。


2
这是一个很好的答案。我认为“始终在私有字段周围使用getter/setter”教条已经导致了Java臃肿的声誉。对字段访问的控制以及对其进行设置或获取时能够做出反应的能力应该成为语言的一部分,并封装在类实现中。 - Alex Worden

19

Getter和Setter的概念是控制类中变量的访问。这样,如果需要在内部更改值以以不同的方式表示它,就可以这样做而不会破坏类外部的任何代码。

例如,假设您有一个距离变量的类,它是以英寸为单位测量的。几个月过去了,您在很多地方使用此类,并突然意识到需要用厘米表示该值。如果您没有使用Getter和Setter,那么您将不得不追踪每个类的用途并进行转换。如果您使用Getter和Setter,只需更改这些方法,使用该类的所有内容都不会受到影响。

public class Measurement
{

    /**
     * The distance in centimeters.
     */
    private double distance;

    /**
     * Gets the distance in inches.
     * @return A distance value.
     */
    public double getDistance()
    {
        return distance / 2.54;
    }

    /**
     * Sets the distance.
     * @param distance The distance, in inches.
     */
    public void setDistance(double distance)
    {
        this.distance = distance * 2.54;
    }
}

1
啊啊啊啊啊!原来是这样!如果你需要以不同的方式表示数据,并且你将在许多其他类中使用变量,只需更改设置方法的实现即可更改所有其他类中的变量。非常好。那么我认为,对于经常使用的变量,使用setter方法是良好的编程习惯,因为您可能以后需要更改它。我们应该多频繁地使用setter和getter方法呢? - Skillet
那是一个不错的例子! - Devesh
追踪或重构使用公共字段的类的每个用法并不难,特别是在现代IDE的帮助下。只需将字段设置为私有,让编译器为您完成繁重的工作即可。至于几个月后可能发生的事情,可以说YAGNI。仅此而已。 - Suketu Bhuta

7

其中一个好处是,您可以通过不实现该字段的setter方法来使字段变为只读ReadOnly


3

以下是缺点。Getter和Setter往往会将你的类的实现细节暴露给外界,这不是好事。想象一下你正在编写汽车维修软件包。因此,你需要一个Car类,并且要对字段公开Getter和Setter。

Date lastOilChangeDate;
int lastOilChangeMileage;

在这个类中,软件想要在客户的汽车需要更换机油时发送电子邮件。

但是,当新款汽车问世时,如果您确定汽车是否需要更换机油的方式与“每3000英里或3个月”不同,会发生什么情况?也许这些新款汽车在油底壳中安装了测量污垢的传感器。显然,您想使用这个来确定是否需要更换机油。
问题在于,您正在通过那些getter/setter解决错误的问题。没有人真正想知道上一次更换机油是什么时候,他们想知道是否需要进行另一次更换。它们只是实现细节,但是您将它们作为接口的一部分。您应该添加一个方法。
public boolean needsOilChange() 

接着,Car类可以按照它想要实现的方式实现。如果算法更改,则Mechanic类不需要更改,因为它只需要needsOilChange方法。


2
只有当你故意编写它们时, getters和setters才会公开实现细节。 - Michael Borgwardt
一辆汽车并不能确定它是否需要换油。这需要由技师进行一些分析或遵循一些指南来确定。你的needsOilChange()方法应该属于其他对象。许多应用程序可能会发现了解上次更换机油的日期和/或里程数非常有用,而这些是每辆汽车都具备的属性。当然,除非我们考虑特斯拉。即使在这种情况下,我们也可能希望保留更换机油的历史记录,因此,这些细节最好存储在其他地方...关键在于,这取决于具体情况。教条主义是敌人。 - Alex Worden
你的汽车太过陈旧了。 - MeBigFatGuy

2

使用getter和setter没有任何问题-只需要注意,通过使用它们并将它们全部设置为public,您会暴露您的变量,并在某种程度上违反了封装。这就是您提到的文章试图警告的内容-不要仅仅自动生成所有私有实例变量的getter和setter;考虑一下您想要向其他类公开什么(以及在什么级别,即private/protected/public),这样您只暴露必要的内容。


-3

这主要用于像下面这样的Java bean。

public Class MyBean{

private int var1;

public void setVar1(int pVar1){

this.var1=pvar1;

}

public int getVar1() {

return var1;

}

}

好处如下:

1. 我们可以实现封装

2. 它被称为 DTO(数据传输对象) 设计模式。 它用于在基于MVC的应用程序中从一层传递数据到另一层。 例如,您可以通过使用getter从表单获取用户输入的数据,然后使用相同的数据插入到数据库中(使用setter),反之亦然。 最新的框架(Spring)提供了内置功能。


1
很抱歉,我认为这不是一个好的答案(代码也没有很好地编写或格式化)。第一点是明显错误的,使用getter和setter并不能帮助您实现封装。第二点关于Java bean被用作DTO是正确的,但问题是关于getter/setter的使用,而bean使用这些并不意味着使用DTO是getter/setter的好处。对我来说,这个回答得-1分,尽管它被接受了。 - Amos M. Carpenter
如果你没有限制地为每个变量使用getter和setter,那么你就没有封装任何东西。 - Robert Rouhani
获取器和设置器是Java Bean的一部分,Java Bean是数据传输对象(DTO),因此根据传递性法则,DTO受益于获取器和设置器。 - Balaswamy Vaddeman
原始的想法可能是将Java beans用作DTO,并且我就是这样做的。 - Balaswamy Vaddeman
@Balaswamy vaddeman:不,那不是最初的想法,正如我所写的。顺便说一下,DTO实际上是一种反模式,基于EJB 2架构的缺陷,现在应该避免使用。 - Michael Borgwardt
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接