获得者和制定者有什么意义?
可能重复:
为什么使用getters和setter?
我已经阅读了关于Java
书籍,说为像x
和y
这样的variables创buildsetter和getter是很好的。 例如:
public int getX(){ return x; } public void setX(int x){ this.x = x; }
但是这和那个有什么不同
...(shape.x)... // basically getX()
和
shape.x = 90; // basically setX()
如果制定者和获得者更好,你能否向我解释会出现什么样的实际问题?
多种原因:
-
如果你允许字段访问像
shape.x = 90
那么您将来无法添加任何逻辑来validation数据。
如果x不能小于100,那么你不能这样做,但是如果你有这样的套路人
public void setShapeValue(int shapeValue){ if(shapeValue < 100){ //do something here like throw exception. } }
- 您不能在写入逻辑上添加类似于副本的内容(请参阅CopyOnWriteArrayList )
- 另一个原因是访问课堂以外的字段,你将不得不将它们标记为公开的,受保护的或默认的,从而失去控制权。 当数据是非常内部的类打破封装和一般的OOPS方法。
虽然像常量一样
public final String SOMETHING = "SOMETHING";
你将允许现场访问,因为他们不能被改变,例如variables,你会把他们与getter,setters。
- 另一种情况是当你希望你的类是不可变的,如果你允许字段访问,那么由于值可以被改变,你打破了你的类的不变性。 但是,如果你仔细devise你的类与getter和没有setter你保持不变的完整性。
虽然在这种情况下,你必须小心getter方法,以确保你不会给出对象的引用(如果你的类有对象作为实例)。
我们可以在任何使用getter和setter的包中使用私有variables。
使用getter和setter函数允许约束和封装。 可以说x是半径。 shape.x = -10没有多大意义。 另外,如果有人试图设置一个非法的值,你可以打印一个错误,设置一个默认值,或者什么都不做。
将成员variables设置为私有是一个很好的习惯,所以不能用程序直接修改它们。
变异函数
封装
很多人都提到封装了实现的细节,这对我来说是在一个类中使用getter和setter的最大原因。 有了这个,你还可以获得许多其他的好处,包括能够随心所欲地抛弃和replace实现,而不需要触及每一段使用你的类的代码。 在一个小项目中,这不是一个很大的好处,但是如果你的代码作为一个使用良好的(内部或公共的)库来结束,那么这将是一个巨大的好处。
一个具体的例子是math中的复数 。 有些语言将它们作为语言或框架function,其他语言则不然。 我将在这里使用一个可变类作为例子,但它可能是一成不变的。
一个复数可以写成forms为a + bi
的实部和虚部,很好地适用于[gs]etRealPart
和[gs]etImaginaryPart
。
然而,在某些情况下,推理极点forms上的复数很容易,给出[gs]etRadius
(r)和[gs]etAngle
(θ)。
您还可以公开像[gs]etComplexNumber(realPart, imaginaryPart)
和[gs]etComplexNumber(radius, angle)
。 根据参数types的不同,这些可能需要也可能不需要不同的名称,但是类的消费者可以使用它们来满足其需要。
这两种forms是可以互换的。 你可以很容易地从一个转换到另一个,所以这个类用于内部存储的forms与那个类的消费者无关。 但是,消费者可以使用任一种forms。 如果你select内部expressionformsa + bi,并且暴露了使用领域而不是getter和setter ,那么你不仅要强迫类的使用者使用这种forms,你也不能随便改变主意,用内部表示来代替重新^(iθ),因为这变得更容易实现在你的特定场景。 你坚持使用你定义的公共API,这些API要求使用特定的字段名称来显示实部和虚部。
我能想到的getter和setter的最好的理由之一是类的API的持久性。 在像Python这样的语言中,你可以通过名字访问成员,并在稍后将其切换到方法。 因为函数的行为不同于java中的成员,只要你访问一个属性就可以了。 限制其范围后来打破了客户端。
通过提供getter和setter,程序员可以灵活地修改成员和行为,只要遵守公共API描述的合约。
下面的例子可以理解用户getter和setter的另一个很好的理由
public class TestGetterSetter{ private String name ; public void setName(String name){ this.name = name ; } public String getName(String name){ return this.name ; } }
getter和setter的意义在于,只有它们被用来访问他们正在获取或设置的私有variables。 这样你提供封装,稍后重构或修改你的代码会容易得多。
想象一下你使用名字而不是它的getter。 然后如果你想添加一些类似于默认的东西(比如默认的名称是'Guest',如果它之前没有设置的话),那么你将不得不修改getter和sayName函数。
public class TestGetterSetter{ private String name ; public void setName(String name){ this.name = name ; } public String getName(String name){ if (this.name == null ){ setName("Guest"); } return this.name ; } }
getter和setter不需要get和set,它们只是普通的成员函数。 然而,这是一个惯例。 (特别是如果您使用Java Beans)
最初,getter / setter模式的创build是为了通过从外部interface
encapsulating
class
的内部来促进良好的面向对象devise。
- 隐藏财产的内部表示
这是你的问题的最佳答案为什么使用getters和setter?
假设,假设你find一个图书馆,可以更好地处理你在自己的课堂上所做的事情(YourClass)。 在这一点上做的自然事情是让YourClass成为该库的包装接口。 它仍然有你的客户端代码需要获取或设置的“X”的概念。 自然,在这一点上,你几乎必须写访问函数。
如果你忽略使用访问函数并让你的客户端代码直接访问YourClass.x,你现在必须重写所有你曾经接触过YourClass.x的客户端代码。 但是,如果从一开始就使用YourClass.getX()和YourClass.setX(),则只需要重写YourClass。
编程的一个关键概念,特别是面向对象的编程,隐藏了实现的细节,所以它们不会被其他类或模块中的代码直接使用。 这样,如果您更改实现细节(如上面的示例中所示),则客户端代码不知道其差异,不必修改。 对于你所有的客户端代码都知道,“x”可能是一个variables,或者它可能是一个即时计算的值。
这是一个过于简单化,并没有涵盖隐藏实现有利的所有场景,但它是最明显的例子。 隐藏实现细节的概念现在与OOP非常紧密地联系在一起,但是在OOP被梦想之前,你可以find它的讨论。 回到软件开发的核心概念之一,就是把一个很大的模糊问题分解成一些小问题,很容易解决。 访问函数有助于保持你的小子任务分离和定义良好:类对彼此内部知道的越less越好。
有很多原因。 这里仅仅是less数。
- 访问者,特别是获取者,经常出现在接口中。 你不能在接口中规定一个成员variables。
- 一旦你暴露这个成员variables,你不能改变你的想法如何实现。 例如,如果以后需要切换到像聚合这样的模式,您希望“x”属性实际上来自某个嵌套对象,则最终必须复制该值并尝试保持其同步。 不好。
- 大多数情况下,你最好不要暴露二传手。 你不能用像x这样的公共字段来做到这一点。
在得到答案之前,我们必须先了解一些事情! “ JavaBeans ”。
JavaBeans是具有属性的Java类 。 对于我们的目的,将属性视为私有实例variables。 因为他们是私人的,所以他们可以从课外访问的唯一途径就是通过课堂上的“方法”。
改变属性值的方法称为setter方法 ,检索属性值的方法称为getter方法 。
我要说的是,getter / setter和public成员都不是很好的面向对象的devise。 他们都破坏OOP封装通过暴露一个对象数据的世界可能不应该访问对象的属性在第一个地方。
这是通过应用OOP的封装原理来完成的。
用于限制对某些对象组件的访问的语言机制。
这意味着,您必须为您的类的属性和方法定义可见性。 有3个共同的可见性:
- 私有:只有类可以看到并使用属性/方法。
- 受保护:只有class级及其子女可以查看和使用属性/方法。
- 公开:每个类都可以看到并使用属性/方法。
在声明私有/受保护属性时,build议您创build方法来获取值(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值只能在类方法中改变,你可以通过在你的代码中调用它来获得实际的大小(而不是改变它):
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封装了一个类的字段,只能通过公共方法访问它们,并将这些值保存为私有的。 这被认为是一个很好的面向对象的原则。
当然,如果只是设置或返回一个值,它通常看起来像是多余的代码。 但是,setters也允许你做inputvalidation或清理。 把它放在一个地方可以提高对象的数据完整性,
因为我们正在使用面向对象的编程语言。 这里我们使用数据隐藏和封装。 variables不应该直接从外部世界访问(为了避免数据隐藏),所以我们将它创build为私有的
shape.x
是不正确的。 getter和setter方法用于获取和设置x的值,这是实现封装的方式。