Java 为什么要使用getter和setter/accessor?

Java 为什么要使用getter和setter/accessor?,java,oop,setter,getter,abstraction,Java,Oop,Setter,Getter,Abstraction,使用getter和setter(只获取和设置)而不是简单地为这些变量使用公共字段有什么好处 如果getter和setter所做的不仅仅是简单的get/set,那么我可以很快地找出这一点,但我并不完全清楚如何: public String foo; 比以下情况更糟: private String foo; public void setFoo(String foo) { this.foo = foo; } public String getFoo() { return foo; } 前者所需的

使用getter和setter(只获取和设置)而不是简单地为这些变量使用公共字段有什么好处

如果getter和setter所做的不仅仅是简单的get/set,那么我可以很快地找出这一点,但我并不完全清楚如何:

public String foo;
比以下情况更糟:

private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }

前者所需的样板代码要少得多。

因为从现在起两周、两个月、几年后,当您意识到setter需要做的不仅仅是设置值时,您还将意识到该属性已直接用于238个其他类:-

访问器和变异器的一个优点是您可以执行验证

例如,如果foo是公共的,我可以轻松地将其设置为null,然后其他人可以尝试调用该对象上的方法。但它已经不在那里了!使用setFoo方法,我可以确保foo从未设置为null


访问器和变异器也允许封装——如果您不希望在值设置之后看到它,那么它可能是在构造函数中设置的,然后由方法使用,但不希望更改,那么任何人都不会看到它。但是,如果您可以允许其他类查看或更改它,则可以提供适当的访问器和/或变体。

OO设计的基本原则之一:封装


它给了你很多好处,其中一个好处是你可以改变场景中的吸收器/设置器的实现,但是只要数据类型保持不变,C++的任何用户都将继续工作。当将字段更改为属性C时,Java或需要重新编译客户端,使用get/set方法更容易修改。例如,向setFoo方法添加验证逻辑不需要更改类的公共接口


在支持真实属性的语言中Python、Ruby或者Smalltalk?获取/设置方法是没有意义的。

此外,这是为了将来证明您的类。特别是,从字段更改为属性是ABI中断,因此,如果您以后决定需要更多的逻辑,而不仅仅是设置/获取字段,那么您需要中断ABI,这当然会给针对您的类编译的任何其他内容带来问题。

原因很多。我最喜欢的一个是当你需要改变行为或者调整你可以设置的变量时。例如,假设您有一个setSpeedint-speed方法。但您希望您只能将最大速度设置为100。您可以执行以下操作:

public void setSpeed(int speed) {
  if ( speed > 100 ) {
    this.speed = 100;
  } else {
    this.speed = speed;
  }
}
public interface LiquidContainer {
  public int getAmountMl();
  public void setAmountMl(int amountMl);
  public int getCapacityMl();
}
现在,如果您在代码中的任何地方都使用公共字段,然后您意识到您需要上述要求,该怎么办?找到公共字段的每一个用法,而不仅仅是修改你的setter


我的2美分:

在支持属性的语言中的另一个用途是setter和getter可以暗示操作是非平凡的。通常,您希望避免在属性中执行任何计算代价高昂的操作。

取决于您的语言。您已经标记了这个面向对象而不是Java,所以我想指出ChssPly76的答案是语言相关的。例如,在Python中,没有理由使用getter和setter。如果需要更改行为,可以使用属性,该属性围绕基本属性访问包装getter和setter。大概是这样的:

类Simpleobject: def_get_valueself: 返回self.\u值-1 定义设置值自身,新值: 自身价值=新价值+1 定义值自身: self.old_value.appendself._value del self.\u值 value=property\u get\u value、\u set\u value、\u del\u value
到目前为止,我在回答中遗漏了一个方面,即访问规范:

对于成员,设置和获取都只有一个访问规范 对于setter和getter,您可以对其进行微调并单独定义 <> P.>实际上有很多很好的理由来考虑使用访问器而不是直接暴露类的字段——而不仅仅是封装的参数,并使将来的更改更容易。p> 以下是我知道的一些原因:

封装与获取或设置属性相关的行为-这允许以后更容易添加验证等附加功能。 隐藏属性的内部表示,同时使用替代表示公开属性。 将公共接口与更改隔离—允许公共接口在实现更改时保持不变,而不会影响现有使用者。 控制属性的生命周期和内存管理语义,在C++或ObjaveC等非托管内存环境中尤为重要。 为属性在运行时更改提供调试截取点—在某些语言中,如果没有此功能,调试属性何时何地更改为特定值可能非常困难 锿。 改进了与设计用于针对属性getter/setter进行操作的库的互操作性—想到了模拟、序列化和WPF。 允许继承者更改属性行为的语义,并通过重写getter/setter方法公开。 允许getter/setter作为lambda表达式而不是值传递。 getter和setter可以允许不同的访问级别,例如get可能是公共的,但是set可以被保护。
它对于延迟加载非常有用。假设所讨论的对象存储在数据库中,除非需要,否则您不想去获取它。如果对象由getter检索,那么内部对象可以为null,直到有人请求它,然后您可以在第一次调用getter时获取它

我在一个交给我的项目中有一个基本页面类,它从两个不同的web服务调用加载一些数据,但是这些web服务调用中的数据并不总是在所有子页面中使用。Web服务,为了所有的好处,开创了slow的新定义,所以如果不需要的话,您不想进行Web服务调用

我从公共字段移动到getter,现在getter检查缓存,如果没有缓存,则调用web服务。因此,只要稍微包装一下,就可以防止大量web服务调用

因此,getter使我不再试图在每个子页面上找出我需要什么。如果我需要它,我会打电话给getter,如果我还没有,它会帮我找到它

    protected YourType _yourName = null;
    public YourType YourName{
      get
      {
        if (_yourName == null)
        {
          _yourName = new YourType();
          return _yourName;
        }
      }
    }

在面向对象语言中,方法及其访问修饰符声明该对象的接口。在构造函数、访问器和mutator方法之间,开发人员可以控制对对象内部状态的访问。如果变量被简单地声明为公共的,那么就没有办法管理这种访问。
当我们使用setter时,我们可以限制用户的输入。表示该变量的馈送将通过一个适当的通道,该通道由我们预定义。所以使用二传手更安全。

很多人都在谈论二传手和二传手的优点,但我想扮演魔鬼代言人的角色。现在我正在调试一个非常大的程序,程序员决定让所有的东西都成为getter和setter。这看起来不错,但却是一场逆向工程的噩梦

假设您正在浏览数百行代码,您会发现:

person.name = "Joe";

在您意识到它是一个setter之前,它只是一段漂亮的代码。现在,您按照该setter进行操作,发现它还设置了person.firstName、person.lastName、person.isHuman、person.hasreallycomonfirstname,并调用person.update,将查询发送到数据库,等等。哦,这就是内存泄漏发生的地方


乍一看,理解一段本地代码是良好可读性的一个重要特性,getter和setter往往会破坏这一特性。这就是为什么我尽量避免使用它们,并尽量减少它们在使用时所做的事情。

我只想抛出注释的想法:@getter和@setter。使用@getter,您应该能够obj=class.field,但不能使用class.field=obj。使用@setter,反之亦然。使用@getter和@setter,您应该能够同时完成这两项任务。这将通过在运行时不调用琐碎的方法来保留封装并减少时间。

我花了相当长的时间考虑Java案例,我相信真正的原因是:

代码到接口,而不是实现 接口只指定方法,不指定字段 换句话说,在接口中指定字段的唯一方法是提供写入新值的方法和读取当前值的方法


那些方法是臭名昭著的“能手”和“能手”…

谢谢,这确实澄清了我的想法。现在,这里有10个不使用getter和setter的理由:

当您意识到您需要做的不仅仅是设置和获取值时,您可以将字段设置为私有字段,这将立即告诉您直接访问它的位置。 您在其中执行的任何验证都只能是上下文无关的,这在实践中很少出现。 您可以更改设置的值-当调用方传递给您一个他们[震惊恐怖]希望您按原样存储的值时,这绝对是一场噩梦。 您可以隐藏内部表示-太棒了,所以您要确保所有这些操作都是对称的,对吗? 您已经将您的公共界面与工作表下的更改隔离开来-如果您正在设计一个界面,并且不确定直接访问某个内容是否合适,那么您应该继续设计。 有些库期望这样,但不是很多——反射、序列化、模拟对象都可以很好地处理公共字段。 继承这个类,您可以覆盖默认功能——换句话说,您不仅会隐藏实现,而且会使其不一致,从而使调用者感到困惑。
最后三个选项我只剩下N/A或D/C…

从面向对象设计的角度来看,这两个选项都会削弱类的封装性,从而损害代码的维护。对于讨论,您可以阅读这篇优秀的文章:

除非当前交付需要,否则不要使用getters setter,也就是说,不要过多考虑将来会发生什么,如果有任何事情需要更改,则大多数生产应用程序、系统中都会有更改请求

思考简单,容易,必要时增加复杂性

我不会仅仅因为我认为这是正确的或者我喜欢这种方法,就利用企业主对深层技术诀窍的无知


我编写了大量的系统,没有getters setter,只有访问修饰符和一些验证n perform biz逻辑的方法。如果你绝对需要的话。使用任何东西。

我只想补充一点,即使有时它们对于变量/对象的封装和安全性是必要的,如果我们想要编写一个真正的面向对象程序,那么我们需要,因为有时候我们在没有必要的时候非常依赖它们,这与我们将变量公开几乎是一样的。

公共字段并不比getter/setter对差,getter/setter对只返回字段并赋值。首先,很明显,在大多数语言中没有功能上的差异。任何差异都必须存在于其他因素中,如可维护性或可读性

经常提到的getter/setter对的一个优点不是。有一种说法是,您可以更改实现,而不必重新编译您的客户机。据推测,setter允许您稍后添加验证等功能,而您的客户甚至不需要知道。然而,向setter添加验证是对其先决条件的更改,违反了之前的约定,很简单,您可以在这里放入任何内容,稍后您可以从getter获得相同的内容

因此,既然您违反了合同,那么更改代码库中的每个文件是您应该做的事情,而不是避免。如果您避免使用它,那么您就假定所有代码都假定这些方法的契约是不同的

如果这不应该是契约,那么接口允许客户端将对象置于无效状态。这与封装完全相反,如果从一开始就不能将该字段设置为任何值,那么为什么从一开始就没有验证呢

同样的论点也适用于这些直通getter/setter对的其他假定优势:如果您以后决定更改所设置的值,那么您就是在违反契约。如果您超越了一些无害的修改(如日志记录或其他不可观察的行为),而覆盖了派生类中的默认功能,那么您就是在破坏基类的契约。这违反了Liskov可替代性原则,这被视为OO的原则之一

如果一个类的每个字段都有这些愚蠢的getter和setter,那么它就是一个没有不变量、没有契约的类。这真的是面向对象的设计吗?如果类只有那些getter和setter,那么它只是一个哑数据保持器,哑数据保持器应该看起来像哑数据保持器:

福班{ 公众: int DaysLeft; 整数; }; 将传递getter/setter对添加到此类中不会增加任何价值。其他类应该提供有意义的操作,而不仅仅是字段已经提供的操作。这就是如何定义和维护有用的不变量

客户:我可以用这个类的对象做什么? 设计器:您可以读写多个变量。 客户:哦。。。我想很酷吧


使用getter和setter是有原因的,但是如果这些原因不存在,那么以虚假封装的名义创建getter/setter对不是一件好事。创建getter或setter的正当理由包括经常提到的可能的更改,如验证或不同的内部表示。或者,该值应该是客户端可读的,但不可写的,例如,读取字典的大小,因此简单的getter是一个不错的选择。但当你做出选择时,这些理由应该存在,而不仅仅是作为你以后可能想要的东西。这是YAGNI的一个实例,您不需要它。

Getter和setter方法是访问器方法,这意味着它们通常是一个公共接口,用于更改私有类成员。使用getter和setter方法定义属性。您可以将getter和setter方法作为类外的属性访问,即使您在类内将它们定义为方法。类之外的属性可以与类中的属性名称具有不同的名称

使用getter和setter方法有一些优点,例如可以创建 具有复杂功能的余烬,您可以访问类似属性的余烬。它们还允许您创建只读和只读属性

尽管getter和setter方法很有用,但您应该小心不要过度使用它们,因为除其他问题外,在某些情况下,它们会使代码维护更加困难。此外,它们还提供对类实现的访问,如公共成员。OOP实践不鼓励直接访问类中的属性

在编写类时,总是鼓励您将尽可能多的实例变量设置为私有,并相应地添加getter和setter方法。这是因为有时您可能不想让用户更改类中的某些变量。例如,如果有一个私有静态方法跟踪为特定类创建的实例数,则不希望用户使用代码修改该计数器。每当调用该变量时,只有构造函数语句应该递增该变量。在这种情况下,您可能会创建一个私有实例变量,并且只允许计数器变量使用getter方法,这意味着用户只能使用getter方法检索当前值,而不能使用setter方法设置新值。创建一个没有setter的getter是使类中的某些变量成为只读的一种简单方法。

代码不断发展。当您需要数据成员保护时,private非常适合。最终,所有的类都应该是一种小型程序,它有一个定义良好的接口,您不能只处理它的内部

这就是说,软件开发并不是要设定课程的最终版本,就好像你在第一次尝试时就按下了一些铁铸的雕像。当您使用它时,代码更像粘土。它会随着您的开发而发展,并了解您正在解决的问题领域的更多信息。在开发过程中,类之间的交互可能会超出您计划分解、合并或拆分的依赖关系。因此,我认为这场辩论归根结底是因为人们不愿意虔诚地写作

int getVar() const { return var ; }
所以你有:

doSomething( obj->getVar() ) ;
而不是

doSomething( obj->var ) ;
getVar不仅在视觉上很嘈杂,而且让人产生一种错觉,认为gettingVar是一个比实际更复杂的过程。作为类作者,您如何看待var的神圣性对于您的类的用户来说尤其令人困惑,如果它有一个passthru setter,那么看起来您设置这些门是为了保护您坚持认为有价值的东西,var的神圣性,但即使你承认var的保护也没有多大价值,因为任何人都可以进来,将var设置为他们想要的任何值,而你甚至不看他们在做什么

因此,假设采用敏捷式方法,我会编写如下程序-即当我编写代码时,不知道它将要做什么/没有时间或经验来规划一个复杂的瀑布式接口集:

1从具有数据和行为的基本对象的所有公共成员开始。这就是为什么在我的C++示例代码中,你会注意到我使用Strut而不是到处的类。 2当数据成员的对象的内部行为变得足够复杂时,例如,它喜欢以某种顺序保留内部std::list,则会编写访问器类型的函数。因为我是自己编程的,所以我并不总是立即将成员设置为私有,但在类的演化过程中,该成员将被提升为受保护或私有

3个完全充实的类,对其内部有严格的规则,即他们确切地知道自己在做什么,并且你不能把技术术语和其内部搞混,这些类会被指定为类,默认为私有成员,只有少数成员可以公开

我发现这种方法可以避免在类演化的早期阶段,当大量数据成员被迁移、转移等时,我坐在那里,虔诚地编写getter/setter。

我们使用getter和setter: 可重用性 在编程的后期阶段执行验证
Getter和setter方法是访问私有类成员的公共接口

封装咒语 封装咒语是使字段私有,方法公共

Getter方法:我们可以访问私有变量

Setter方法:我们可以修改私有字段

即使getter和setter方法没有添加新的功能,我们也可以改变主意,稍后再来创建该方法

较好的 更安全的;和 更快。 任何可以使用值的地方,都可以添加返回该值的方法。而不是:

int x = 1000 - 500
使用

用外行的话说 假设我们需要存储此人的详细信息。此人具有姓名、年龄和性别字段。这样做需要创建名称、年龄和性别的方法。现在,如果我们需要创建另一个人,那么有必要创建 姓名、年龄、性别都要重来一遍


我们可以使用getter和setter方法创建一个bean classPerson,而不是这样做。因此,明天我们可以在需要添加一个新的人时创建这个bean类对象的对象,我们可以重新使用bean类的字段和方法,这是更好的。

有一个很好的理由考虑使用访问器,没有属性继承。请参见下一个示例:

public class TestPropertyOverride {
    public static class A {
        public int i = 0;

        public void add() {
            i++;
        }

        public int getI() {
            return i;
        }
    }

    public static class B extends A {
        public int i = 2;

        @Override
        public void add() {
            i = i + 2;
        }

        @Override
        public int getI() {
            return i;
        }
    }

    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.i);
        a.add();
        System.out.println(a.i);
        System.out.println(a.getI());
    }
}
输出:

0
0
4

在以下情况下,应使用getter和setter:

您处理的是概念上属于属性的内容,但是: 您的语言没有属性或类似的机制,比如Tcl的变量跟踪,或者 您的语言的属性支持不足以支持此用例,或者 您的语言或有时您的框架的惯用约定鼓励此用例的getter或setter。 所以这很少是一个一般的OO问题;这是一个特定于语言的问题,对于不同的语言和不同的用例有不同的答案

从OO理论的角度来看,getter和setter是无用的。类的接口是它所做的,而不是它的状态。如果不是,你写错了类。在非常简单的情况下,类所做的只是,例如,在直角坐标中表示一个点,*属性是接口的一部分;能手和二传手只是把这件事弄得一团糟。但在非常简单的情况下,属性、getter和setter都不是接口的一部分

换句话说:如果你认为你的类的消费者甚至不应该知道你有一个垃圾邮件属性,更不用说能够随意改变它,那么给他们一个set_spam方法是你最不想做的事情

*即使对于这个简单的类,也不一定要允许设置x和y值。如果这真的是一个类,那么它不应该有诸如translate、rotate等方法吗。?如果它只是一个类,因为您的语言没有记录/结构/命名元组,那么这实际上不是一个OO问题

但是从来没有人做过一般的OO设计。他们用特定的语言进行设计和实现。在某些语言中,getter和setter远非无用

如果您的语言没有属性,那么表示概念上是属性,但实际上是计算或验证的属性的唯一方法是通过getter和setter

即使您的语言确实有属性,也可能存在这些属性不足或不适当的情况。例如,如果希望允许子类控制属性的语义,在没有动态访问的语言中,子类不能用计算属性替换属性

至于问题,如果我以后想更改我的实现怎么办?在OP的问题和公认的答案中以不同的措辞重复多次的问题:如果这确实是一个纯粹的实现更改,并且您从一个属性开始,那么您可以将其更改为属性,而不会影响接口。当然,除非你的语言不支持这一点。所以这又是同样的情况

此外,遵循您正在使用的语言或框架的习惯用法也很重要。如果你用C写漂亮的Ruby风格的代码,那么除了你以外的任何有经验的C开发人员都会在阅读上遇到困难,这很糟糕。一些语言在其约定方面比其他语言具有更强的文化。-Java和Python恰好有两种最强大的文化,这可能不是巧合,因为它们在惯用的getter方面处于对立的两端


除了人类读者之外,还会有图书馆和工具期望你遵守惯例,如果你不遵守惯例,你的生活会变得更加艰难。将Interface Builder小部件与ObjC属性以外的任何内容挂钩,或者使用某些Java模拟库而不使用getter,这只会让您的生活更加困难。如果这些工具对您很重要,就不要与它们对抗。

在纯面向对象的世界中,getter和setter是一种可怕的反模式。阅读本文:。简言之,它们鼓励程序员将对象视为数据结构,这种思维方式与COBOL或C中的纯过程思维类似。在面向对象语言中,没有数据结构,只有暴露行为而不是属性/属性的对象


你可以在我关于面向对象编程的书的第3.5节中找到更多关于它们的信息。

编辑:我回答这个问题是因为有很多人在学习编程时问这个问题,而且大多数答案在技术上都很有能力,但如果你是新手,它们就不那么容易理解。我们都是新手,所以我想我应该尝试一个更为新手友好的回答

两个主要的问题是多态性和验证。即使它只是一个愚蠢的数据结构

假设我们有一个简单的类:

public class Bottle {
  public int amountOfWaterMl;
  public int capacityMl;
}
一个非常简单的类,它可以容纳多少液体 id在里面,它的容量是多少,以毫升为单位

当我这样做时会发生什么:

Bottle bot = new Bottle();
bot.amountOfWaterMl = 1500;
bot.capacityMl = 1000;
好吧,你不会指望那会起作用的,对吧? 你想做些精神检查。更糟糕的是,如果我从来没有指定最大容量呢?哦,天哪,我们有个问题

但还有另一个问题。如果瓶子只是一种容器呢?如果我们有几个容器,所有的容器都装满了容量和数量的液体呢?如果我们可以制作一个界面,我们就可以让程序的其他部分接受这个界面,瓶子、饮料罐和各种各样的东西就可以互换工作了。那不是更好吗?由于接口需要方法,这也是一件好事

我们最终会得到这样的结果:

public void setSpeed(int speed) {
  if ( speed > 100 ) {
    this.speed = 100;
  } else {
    this.speed = speed;
  }
}
public interface LiquidContainer {
  public int getAmountMl();
  public void setAmountMl(int amountMl);
  public int getCapacityMl();
}
太好了!现在我们把瓶子换成这个:

public class Bottle extends LiquidContainer {
  private int capacityMl;
  private int amountFilledMl;

  public Bottle(int capacityMl, int amountFilledMl) {
    this.capacityMl = capacityMl;
    this.amountFilledMl = amountFilledMl;
    checkNotOverFlow();
  }

  public int getAmountMl() {
    return amountFilledMl;
  }

  public void setAmountMl(int amountMl) {
     this.amountFilled = amountMl;
     checkNotOverFlow();
  }
  public int getCapacityMl() {
    return capacityMl;
  }

  private void checkNotOverFlow() {
    if(amountOfWaterMl > capacityMl) {
      throw new BottleOverflowException();
    }
}
我将把BottleOverflowException的定义留给读者作为练习


现在请注意这是多么健壮。我们现在可以通过接受LiquidContainer而不是瓶子来处理代码中的任何类型的容器。这些瓶子如何处理这类东西可能各不相同。您可以使用瓶子在磁盘发生变化时将其状态写入磁盘,也可以使用瓶子保存在SQL数据库或GNU上,以了解其他情况

所有这些都有不同的方法来处理各种各样的问题。瓶子只是检查,如果溢出,就会抛出RuntimeException。但这样做可能是错误的。 关于错误处理有一个有用的讨论,但我在这里故意保持它非常简单。评论中的人可能会指出这种简单化方法的缺陷

是的,我们似乎从一个非常简单的想法快速得到了更好的答案

请注意,您不能更改瓶子的容量。它现在已经变成了石头。可以通过声明int为final来实现这一点。但如果这是一个列表,您可以清空它,向其中添加新的内容,等等。你不能限制接触内脏

还有第三件事不是每个人都能解决的:getter和setter使用方法调用。这意味着它们看起来像其他地方的普通方法。DTO和其他东西没有奇怪的特定语法,到处都有相同的东西。

getter和setter用于实现面向对象编程的两个基本方面:

抽象 封装 假设我们有一个Employee类:

package com.highmark.productConfig.types;

public class Employee {

    private String firstName;
    private String middleName;
    private String lastName;

    public String getFirstName() {
      return firstName;
    }
    public void setFirstName(String firstName) {
       this.firstName = firstName;
    }
    public String getMiddleName() {
        return middleName;
    }
    public void setMiddleName(String middleName) {
         this.middleName = middleName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFullName(){
        return this.getFirstName() + this.getMiddleName() +  this.getLastName();
    }
 }

这里,全名的实现细节对用户是隐藏的,用户无法直接访问,这与公共属性不同

我知道有点晚了,但我认为有些人对表演感兴趣

我做了一个小小的性能测试。我写了一个类NumberHolder,它包含一个整数。您可以使用getter方法读取该整数 anInstance.getNumber或使用anInstance.number直接访问数字。我的程序通过两种方式读取100000000次数字。该过程重复五次,并打印时间。我得到了以下结果:

Time 1: 953ms, Time 2: 741ms
Time 1: 655ms, Time 2: 743ms
Time 1: 656ms, Time 2: 634ms
Time 1: 637ms, Time 2: 629ms
Time 1: 633ms, Time 2: 625ms
时间1是最直接的方式,时间2是最有效的方式

你看,getter几乎总是快一点。然后我尝试了不同的循环次数。我用了1000万和10万,而不是100万。 结果是:

1000万个周期:

Time 1: 6382ms, Time 2: 6351ms
Time 1: 6363ms, Time 2: 6351ms
Time 1: 6350ms, Time 2: 6363ms
Time 1: 6353ms, Time 2: 6357ms
Time 1: 6348ms, Time 2: 6354ms
在1000万次循环中,时间几乎相同。 以下是10万个10万个循环:

Time 1: 77ms, Time 2: 73ms
Time 1: 94ms, Time 2: 65ms
Time 1: 67ms, Time 2: 63ms
Time 1: 65ms, Time 2: 65ms
Time 1: 66ms, Time 2: 63ms

同样,对于不同数量的循环,getter比常规方法快一点。我希望这对您有所帮助。

getter/setter的一个相对现代的优点是,可以更轻松地在标记索引代码编辑器中浏览代码。例如,如果要查看谁设置了成员,可以打开setter的调用层次结构


另一方面,如果成员是公共的,则这些工具无法过滤对该成员的读/写访问。因此,您必须费力地使用该会员。

我坐在那里,盯着一个500k线路的应用程序,而这个应用程序从来都不需要。这就是说,如果需要一次,它将开始导致维护噩梦。这对我来说已经足够好了。我只能羡慕你和你的应用程序:-也就是说,这也取决于你的软件堆栈。比如Delphi和C-我想?允许您将属性定义为一级公民,在这里,他们最初可以直接读/写字段,但如果需要,也可以通过getter/setter方法来读/写字段。非常方便。遗憾的是,Java没有——更不用说javabeans标准,它迫使您同时使用getter/setter,许多编程环境和编辑器现在都支持在IDE中进行重构,或者作为免费的外接程序进行重构,这在某种程度上减少了问题的影响。听起来像是预成熟的优化当您想知道是否要实现getter和setter时,您需要问的问题是:用户为什么要

一个类的成员是否需要访问该类的内部?不管他们是直接执行还是被一个很薄的伪层屏蔽,这都无关紧要——如果用户需要访问实现细节,那么这表明该类没有提供足够的抽象。另请参见。@Dean J:Duplicate和许多其他问题:当然,当对象不需要更改属性时,这两个问题同样糟糕。我宁愿把所有东西都私有化,然后在有用的时候添加getter,在需要的时候添加setter。如果您碰巧正在编写有状态的可变对象,那么它们是非常重要的。是的,我在回答下面的评论中也说过同样的话。Java不是唯一使用getter/setter作为支撑的语言,就像Python不是唯一能够定义属性的语言一样。但是,主要的一点仍然是-property不是同一个公共字段。@jcd-一点也不是。在这里,通过公开公共字段来定义接口公共API将是一个更好的术语。一旦完成,就再也回不去了。属性不是字段,因为它们为您提供了一种机制,可以通过将字段路由到已定义的方法来拦截访问字段的尝试;不过,这只不过是语法糖化了getter/setter方法。这非常方便,但不会改变基本的范例-暴露字段而不控制对字段的访问违反了封装原则。@ChssPly76-I不同意。我可以像控制属性一样控制它们,因为我可以随时将它们设置为属性。使用样板getter和setter的属性与原始属性之间没有区别,只是原始属性更快,因为它使用底层语言,而不是调用方法。在功能上,它们是相同的。违反封装的唯一方法是,如果您认为括号obj.set_attr'foo'天生优于等号obj.attr='foo'。Public access是Public access。@jcdayer有很多控制权,但可读性不强,其他人经常错误地认为obj.attr='foo'只设置变量,而不设置任何其他内容happening@TimoHuovinen这与Java中的用户假设obj.setAttr'foo'只设置变量而不发生任何其他事情有何不同?如果它是一个公共方法,那么它就是一个公共方法。如果您使用它来实现一些副作用,并且它是公开的,那么您最好能够依靠一切工作,就好像只有预期的副作用发生了一样,所有其他实现细节和其他副作用、资源使用等等都隐藏在用户的顾虑之外。这与Python没有什么不同。Python实现这种效果的语法非常简单,查找公共字段的每一个用法应该不会那么困难。让它私有化,让编译器找到它们。这当然是真的,但为什么要比设计的更难呢。get/set方法仍然是更好的答案。@Nathan:找到其他用法不是问题。全部更改是有好处的。@GraemePerrow必须全部更改它们是一个优势,而不是一个问题:如果您的代码假定速度可以高于100,因为,您知道,在您违反合同之前,它可以!当速度<200时{做点什么;加速;}这是一个非常糟糕的例子!应该有人打电话:myCar.setSpeed157;几行之后,速度=myCar.getSpeed;现在。。。我希望您能够愉快地调试,同时尝试理解为什么速度==100,而它应该是157Re:C。如果您将功能添加到get/set,那么这不需要重新编译吗?@steamer25:抱歉,输入错误。我的意思是必须重新编译类的客户端。向setFoo方法添加验证逻辑不需要在语言级别更改类的接口,但它确实更改了实际接口,即契约,因为它更改了前提条件。为什么不希望编译器将其视为破坏性更改呢?@R.MartinhoFernandes如何修复这个破坏性问题?编译器无法判断它是否正在中断。这只是当你为他人编写库时的一个问题,但你把它变成了通用的僵尸——这里是龙!如答案中所述,需要重新编译是编译器使您意识到可能发生的破坏性更改的一种方式。我写的几乎所有东西实际上都是别人的图书馆,因为我不是一个人工作。我编写的代码具有项目中其他人将使用的接口。有什么区别?见鬼,即使我将是这些接口的用户,为什么我要让我的代码保持较低的质量标准?我不喜欢使用麻烦的接口,即使是我自己编写的;如果是一个项目,您没有向任何人导出源代码,并且您对源代码拥有完全控制权。。。你是g吗

在任何非平凡的Java项目中,您都需要编写接口代码,以使事情易于管理和可测试,比如模型和代理对象。如果使用接口,则需要getter和setter。目前正在重构一个大型代码库,这是一场噩梦。getter和setter做得太多了,包括调用其他非常繁忙的getter和setter,最终将可读性降低为零。验证是使用访问器的一个很好的理由,但使用它们做更多的事情似乎会消除任何潜在的好处。这是反对语法糖的论点,而不是反对一般的设置器。真的,真的,而且,它指出了为什么单个属性设置器打开了无效状态的大门,却完全没有任何补救措施来确保允许抽象泄漏的任何给定对象的完整性。在你意识到它是一个设置器之前,它只是一段漂亮的代码——嘿,原来的帖子是关于Java还是关于C的关于可读性,我完全同意。当person.name=Joe;被称为。在信息方面并没有任何瑕疵,语法高亮显示显示它是一个字段,重构更简单,不需要重构两个方法和一个字段。第二,所有那些认为getIsValid或者应该是isValid的浪费大脑周期都可以被遗忘。我想不出有哪一次我被一个getter/setter从一个bug中拯救出来了。getter和setter提供的封装非常薄。请参阅。如果您的公共接口声明“foo”的类型为“T”,并且可以设置为任何值,则您永远无法更改它。后一种情况下,您不能决定将其设置为“Y”类型,也不能强加诸如大小限制之类的规则。因此,如果您的公共get/set没有做太多的set/get,那么您就没有得到公共字段不会提供的任何东西,并且使用起来更加麻烦。如果你有一个约束,比如对象可以设置为null,或者值必须在一个范围内,那么是的,需要一个公共集方法,但是你仍然通过设置这个不能更改的值来呈现一个契约为什么契约不能更改?如果契约更改,为什么要继续编译?我认为关键的论点是,如果您正在设计一个界面,并且不确定直接访问某个东西是否合适,那么您应该继续设计。这是getter/setter最重要的问题:它们将类简化为或多或少包含公共字段的容器。然而,在真正的OOP中,对象不仅仅是数据字段的容器。它封装了状态和操作该状态的算法。这句话的关键是状态应该被封装,并且只能由对象提供的算法来操作。我认为改变getter或setter的行为并不是一个突破性的改变@R.MartinhoFernandes不一定总是这样。第比利斯:很好的答案+1。我唯一的批评是,我花了好几次阅读,才弄清楚最后一段中的验证与前几段中的验证有何不同,前几段中的验证是你在后一种情况下放弃的,但在前一段中推广的;在这方面,调整措辞可能会有所帮助。这是一个很好的答案,但遗憾的是,当今时代已经忘记了信息隐藏是什么或它的用途。他们从来没有读过关于不变性的书,在追求最敏捷的过程中,从来没有画过状态转换图来定义一个对象的合法状态是什么,因此什么不是。呃,我猜你不知道什么是类不变性。如果它是一个非平凡的结构,那就意味着它有不变量需要维护,如果没有这些不变量,我们就无法设计它。有一个错误,一个成员更新错误。而且读起来就像是成员更新违反了类不变量。设计良好的类不允许客户端代码违反其不变量。+1表示“哑数据持有者应该看起来像哑数据持有者”。不幸的是,如今每个人似乎都在围绕哑数据持有者设计一切,许多框架甚至需要它…+1表示伟大的答案,但是哑数据保持器中的getter和setter有一个优点,而且也只有一个优点:大多数编译语言的IDE都有一个内置选项来查找方法或属性的所有引用。使用过滤掉的getter查找所有设置数据的位置,多年来节省了我大量的时间,我很难想象没有getter调试复杂的代码。它将在运行时用简单的方法实现。事实上,这可能很重要。今天,这可以通过注释预处理器来完成。那么getter是否调用setter呢?我已经添加了一个代码示例,说明我过去是如何完成的-本质上,您将实际类存储在受保护的成员中,然后在get访问器中返回该受保护的成员,如果没有初始化,就初始化它。它的C getter和setter建议一个贫乏的域模型
.有趣的观点。但在大多数编程环境中,我们需要的是数据结构。以链接文章的狗为例。是的,你不能通过设置属性来改变现实世界中狗的体重。。。但是一只新狗不是狗。它是保存狗的信息的对象。对于这种用法,能够纠正错误记录的重量是很自然的。好吧,我告诉你,大多数有用的程序不需要建模/模拟真实世界的对象。在我看来,这根本不是关于编程语言的。这与我们编写程序的目的有关。不管现实世界与否,耶戈尔完全正确。如果您真正拥有的是一个结构,并且不需要编写任何按名称引用它的代码,请将其放入哈希表或其他数据结构中。如果您确实需要为它编写代码,那么将其作为类的成员,并将操作该变量的代码放在同一个类中,并且省略setter和getter。注:虽然我大部分都同意yegor的观点,但我已经开始相信,没有代码的带注释bean在某种程度上是有用的数据结构,而且有时需要getter,setter不应该存在。我被这个完整的答案迷住了,尽管它是正确的和相关的,但并没有直接解决这个问题。也许应该说setter/getter和public变量都是错误的。。。绝对具体地说,永远不要使用setter和可写公共变量,而getter与公共final变量几乎相同,有时是一个必要的缺点,但两者都不比另一个好。。。。这是一个定义良好的接口,您不能只处理setter的内部结构和验证。感谢我读到的前半部分对接口的合理解释,没有对远程控制或车辆的任何引用。您可以使用瓶子在更改状态时将其状态写入磁盘,或者使用瓶子保存在SQL数据库上,我非常喜欢xD。但无论如何,展示它是个好主意!我从来没有期望一个getter或setter是一个昂贵的操作。在这种情况下,最好使用工厂:使用您需要的所有setter,然后调用昂贵的execute或build方法。使用函数调用来访问内存,而不是简单地加载对象地址并添加偏移量来访问成员,这会带来明显的开销。无论如何,VM平台都有可能优化您的getter。无论如何,上面提到的开销不值得失去getter/setter的所有好处。对我来说,拥有大量的getter和setter没有任何独特的功能是无用的。getFullName是一个例外,因为它执行其他操作。只有三个变量是公共的,然后保持getFullName将使程序更易于阅读,但仍然隐藏了fullname。一般来说,如果a。他们做一些独特的和/或b。你只有一个,是的,你可以有一个公开的最终版本,但是这样做的好处是你可以在不改变接口的情况下改变类的内部结构。比方说,您没有三个属性,而是有一个-字符串数组。如果您一直在使用getter和setter,您可以进行更改,然后更新getter/setter,使其知道名称[0]是第一个名称,名称[1]是中间的,等等。但是如果您只使用公共属性,您还必须更改每个访问的类,因为他们一直使用的firstName属性已经不存在了。@Andrews从我在现实生活中看到的情况来看,当人们改变类的内部结构时,他们也会改变接口,并对所有代码进行大的重构。你可以做正确的clic>在成员上查找用法,就像在getter/setter上一样