What is the point of getters and setters?

Possible Duplicate:
Why use getters and setters?

I have read books on Java, saying that it is good to create setters and getters for variables such as x and y. For example:

public int getX(){
return x;
}


public void setX(int x){
this.x = x;
}

But what is the difference from that and

...(shape.x)...   // Basically getX()

and

shape.x = 90;    // Basically setX()

If setters and getters are better, what practical problems would arise?

98829 次浏览

使用 getter 和 setter 函数允许约束和封装。假设 x 是半径。X = -10没有多大意义。此外,如果有人试图设置非法值,您可以打印错误,设置默认值,或什么都不做。

最好将成员变量设置为私有,这样使用它们的程序就不能直接修改它们。

变异功能
封装

原因有很多,以下只是其中的一小部分。

  1. 访问器,特别是 getter,经常出现在接口中。
  2. 一旦公开了这个成员变量,就不能改变它的实现方式。例如,如果您以后需要切换到类似聚合的模式,您希望“ x”属性实际来自某个嵌套对象,那么您最终必须复制该值并尝试使其保持同步。情况不妙。
  3. 大多数时候,你是更好的关闭 没有曝光二传手。对于像 x 这样的公共字段不能这样做。

多重原因:

  • 如果允许字段访问,如

    X = 90

那么您就不能在将来添加任何逻辑来验证数据。

如果 x 不能小于100你就不能做,但是如果你有像

public void setShapeValue(int shapeValue){
if(shapeValue < 100){
//do something here like throw exception.
}
}
  • 您不能在写逻辑上添加类似复制的内容(请参阅 CopyOnWriteArrayList)
  • 另一个原因是访问类以外的字段时,必须将它们标记为 public、 protected 或 default,因此会失去控制。当数据是非常内部的类破坏 封装和一般 哎呀方法。

虽然对于像

public final String SOMETHING = "SOMETHING";

你将允许字段访问,因为它们不能被更改,因为实例变量你将把它们与 getters,setter 放在一起。

  • 另一种情况是,当您希望您的 Class 是不可变的时候,如果您允许字段访问,那么您就打破了类的不可变性,因为值可以更改。但是,如果仔细地使用 getters 而不使用 setter 设计类,则可以保持不变性的完整性。

尽管在这种情况下,在 getter 方法中必须小心,以确保不会提供对象的引用(以防类将对象作为实例)。

我们可以使用 getter 和 setter 在任何包中使用私有变量。

我认为 getters/setter 和 public 成员都不是好的面向对象设计。它们都破坏了 OOP 封装,因为它们将对象数据暴露给世界,而这些对象数据可能本来就不应该访问对象的属性。

这是通过应用面向对象的 封装原则实现的。

一种语言机制,用于限制对某些对象组件的访问。

这意味着,您必须为类的属性和方法定义可见性:

  • Private: 只有类可以查看和使用属性/方法。
  • 受保护: 只有类及其子类可以查看和使用属性/方法。
  • Public: 每个类都可以查看和使用属性/方法。

当声明 private/protected 属性时,建议您创建方法来获取值(get)并更改值(set)。关于可见性的一个例子是 [ArrayList][2]类: 它有一个 size属性来知道内部数组的实际大小。只有类 必须的更改了它的值,因此代码类似于

public class ArrayList<E> {
private int size;
private Object[] array;
public getSize() {
return this.size;
}
public void add(E element) {
//logic to add the element in the array...
this.size++;
}
}

在这个示例中,您可以看到 size 值只能在类方法中更改,并且您可以通过在代码中调用它(而不是变更它)来获得实际的 size:

public void someMethod() {
List<String> ls = new ArrayList<String>();
//adding values
ls.add("Hello");
ls.add("World");
for(int i = 0; i < ls.size(); i++) {
System.out.println(ls.get(i));
}
}

Getters 和 setter 封装了类的字段,使它们只能通过类的公共方法访问,并保持值本身是私有的。这被认为是一个很好的面向对象原则。

当然,如果它只是设置或返回一个值,那么它通常看起来就像是冗余代码。但是,setter 也允许您进行输入验证或清理。将它放在一个位置可以提高对象的数据完整性,

在得到答案之前,我们必须先知道一些事情... ! “ JavaBeans”。
JavaBean 是具有属性 的 Java 类。对于我们的目的,可以将属性看作私有实例变量。因为它们是私人的,只有这样才能访问它们 从他们的类的外部是通过’方法’在类。
更改属性值的方法称为 赛特方法赛特方法,检索属性值的方法称为 Getter 方法

因为我们使用的是面向对象的编程语言。 这里我们使用数据隐藏和封装。 变量不应该直接从外部世界访问(为了实现数据隐藏) ,因此我们将创建它私有的

Shape.x

是不对的。 使用 Getter 和 setter 方法获取和设置 x 的值,这是实现封装的方法。

我能想到的使用 getter 和 setter 的最佳原因之一是类的 API 的永久性。在 Python 等语言中,您可以通过成员的名称访问它们,并在以后将它们切换到方法。因为一旦访问了属性,函数的行为就不同于 Java 中的成员。稍后限制其范围将中断客户端。

通过提供 getter 和 setter,程序员可以灵活地自由修改成员和行为,只要遵守公共 API 描述的契约即可。

让我们假设,您找到了一个比您在自己的类(YourClass)中所做的工作更好的库。此时自然要做的事情是将 YourClass 作为到该库的包装器接口。它仍然有一个“ X”的概念,您的客户机代码需要获取或设置这个概念。当然,在这一点上,您几乎必须编写访问器函数。

如果忽略使用访问器函数并让客户机代码直接访问 YourClass.x,那么现在就必须重写所有曾经接触过 YourClass.x 的客户机代码。但是如果从一开始就使用 YourClass.getX ()和 YourClass.setX () ,那么只需要重写 YourClass。

编程的一个关键概念,特别是面向对象编程,是隐藏实现细节,这样它们就不会被其他类或模块中的代码直接使用。这样,如果您更改了实现细节(如上面的示例所示) ,客户机代码就不会知道其中的区别,也不需要进行修改。对于您的客户机代码来说,“ x”可能是一个变量,也可能是一个动态计算的值。

这是一个过于简单化的例子,并没有涵盖隐藏实现有益的所有场景,但这是最明显的例子。隐藏实现细节的概念现在与 OOP 紧密相连,但是您可以发现关于它的讨论可以追溯到 OOP 被构想出来之前的几十年。它可以追溯到软件开发的核心概念之一,即把一个大的模糊问题分解成定义明确、易于解决的小问题。访问器函数有助于保持小型子任务的独立性和良好定义: 类对彼此内部的了解越少越好。

用户 gettersetter的另一个好的理由可以通过下面的例子来理解

public class TestGetterSetter{
private String name ;




public void setName(String name){
this.name = name ;
}




public String getName(){
return this.name ;
}
}

Getter 和 setter 的要点是,只有它们用于访问它们正在获取或设置的私有变量。通过这种方式可以提供封装,以后重构或修改代码将更加容易。

假设您使用 name 而不是它的 getter。然后,如果您想添加类似于缺省值的东西(假设缺省名称是“ Guest”,如果之前没有设置的话) ,那么您必须修改 gettersayName函数。

public class TestGetterSetter{
private String name ;




public void setName(String name){
this.name = name ;
}




public String getName(){
if (this.name == null ){
setName("Guest");
}
return this.name ;
}
}

Getter 和 setter 不需要从 get 和 set 开始——它们只是普通的成员函数。不管怎样,这是一个惯例。(特别是如果你使用爪哇豆)

很多人提到过封装实现的细节,对我来说,这是在类中使用 getter 和 setter 的最大原因。通过这种方式,您还可以获得许多其他好处,包括可以随心所欲地抛弃和替换实现,而无需触及使用您的类的每一段代码。在一个小项目中,这不是一个很大的好处,但是如果您的代码最终成为一个使用良好的(内部或公共)库,那么它可以成为 巨大的一个好处。

一个具体的例子: 数学中的复数。有些语言将它们作为语言或框架特性,有些则没有。在这里,我将使用一个可变类作为示例,但它也可以很容易地成为不可变的。

一个复数可以写在形式 a + bi与实数和虚数部分,借给自己很好的 [gs]etRealPart[gs]etImaginaryPart

然而,在某些情况下,很容易推断极形 re^(iθ)上的复数,给出 [gs]etRadius(r)和 [gs]etAngle(θ)。

您还可以公开诸如 [gs]etComplexNumber(realPart, imaginaryPart)[gs]etComplexNumber(radius, angle)之类的方法。根据参数类型的不同,这些类可能需要也可能不需要不同的名称,但是类的使用者可以根据自己的需要使用这两个名称中的任意一个。

这两种形式是可互换的; 您可以相当容易地从一种形式转换为另一种形式,因此类用于内部存储的形式与该类的使用者无关。不过,消费者可以使用任何一种形式。如果你选择内部表示形式 a + bi 和 使用字段而不是 getter 和 setter,不仅会迫使类消费者使用这种形式,而且你以后也不能轻易改变主意,用 re ^ (iθ)替换内部表示形式,因为这在你的特定场景中更容易实现。您必须使用已定义的公共 API,该 API 要求使用特定的字段名公开实际的和虚构的部分。