Java 的公共字段是怎么回事?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7959129/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What's the deal with Java's public fields?
提问by AnnanFay
I've been reading two articles (1)(2)on javaworld.com about how all class fields should be private and getter/setter methods are just as bad. An object should act on the data it has rather than allowing access to it.
我一直在阅读javaworld.com 上的两篇文章(1) (2),关于所有类字段都应该是私有的,而 getter/setter 方法也同样糟糕。一个对象应该对其拥有的数据进行操作,而不是允许对其进行访问。
I'm currently working on a University assignment for Connect Four. In designing the program the Agents playing the Game need access to the Board's state (so they can decide what to move). They also need to pass this move to the Game so it can validate it as a legal move. And during deciding what to move pieces are grouped into Threats with a start and end Points.
我目前正在为Connect Four进行大学作业。在设计程序时,玩游戏的代理需要访问董事会的状态(以便他们可以决定移动什么)。他们还需要将此举动传递给游戏,以便它可以将其验证为合法举动。在决定移动什么的过程中,碎片被分组为具有起点和终点的威胁。
Board, Threat and Point objects don't really do anything. They are just there to store related data that can be accessed in a human readable way.
Board、Threat 和 Point 对象实际上并没有做任何事情。它们只是用来存储可以以人类可读的方式访问的相关数据。
At the start of design I was representing Points on the board as two element int arrays, however that got annoying when creating points or referencing components of them.
在设计之初,我将板上的点表示为两个元素 int 数组,但是在创建点或引用它们的组件时这很烦人。
So, the class:
所以,类:
public class Point {
public int x;
public int y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
}
Perfect in every way I can think of. Except it breaks every rule I've learned. Have I sinned?
在我能想到的各个方面都很完美。除了它打破了我学到的每一条规则。我犯了罪吗?
采纳答案by meriton
Public fields expose the representation of an object to its callers, i.e. if the representation has to change, so do the callers.
公共字段向其调用者公开对象的表示,即如果表示必须更改,则调用者也必须更改。
By encapsulating the representation, you can enforce how callers interact with it, and can change that representation without having to modify the callers provided the public api is unchanged. In any non-trivial program, encapsulation is necessary to achieve reasonable maintainability. However, while you need capsules, their proper granularity may be larger than a single class. For instance, it makes little sense to encapsulate an Iterator
from the internal representation of the Collection
it operates on.
通过封装表示,您可以强制调用者如何与其交互,并且可以更改该表示而无需修改调用者,前提是公共 api 不变。在任何非平凡的程序中,为了实现合理的可维护性,封装是必要的。但是,当您需要胶囊时,它们的适当粒度可能比单个类大。例如,Iterator
从Collection
它所操作的的内部表示中封装 an 是没有意义的。
With that out of the way, let's look at your example:
有了这个,让我们看看你的例子:
public class Point {
public int x;
public int y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
}
The internal representation of that class is exceedingly unlikely to change, so hiding the structure of the representation by making the fields private has no benefit. However, I'd prevent callers from modifying a Point
once it has been constructed:
该类的内部表示极不可能改变,因此通过将字段设为私有来隐藏表示的结构没有任何好处。但是,我会阻止调用者Point
在构建后修改它:
public class Point {
public final int x;
public final int y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
}
so that a class that actually wishes to encapsulate its state can return its Point
without leakingits internal representation, and use a given Point in its representation without capturingit. This also fits nicely with the mathematical notion of a point, which has no identity or changing state.
这样一个真正希望封装其状态的类可以在Point
不泄漏其内部表示的情况下返回其状态,并在其表示中使用给定的 Point 而不捕获它。这也非常符合点的数学概念,点没有恒等性或状态变化。
In designing the program the Agents playing the Game need access to the Board's state (so they can decide what to move). They also need to pass this move to the Game so it can validate it as a legal move. And during deciding what to move pieces are grouped into Threats with a start and end Points.
Board, Threat and Point objects don't really do anything. They are just there to store related data that can be accessed in a human readable way.
在设计程序时,玩游戏的代理需要访问董事会的状态(以便他们可以决定移动什么)。他们还需要将此举动传递给游戏,以便它可以将其验证为合法举动。在决定移动什么的过程中,碎片被分组为具有起点和终点的威胁。
Board、Threat 和 Point 对象实际上并没有做任何事情。它们只是用来存储可以以人类可读的方式访问的相关数据。
Now this sounds like a wasted opportunity for encapsulation: The agents should really not be permitted to arbitrarily modify the board, but be restricted to legal moves. Why is it the responsibility of class Game
to decide what a legal move is, when the state being updated resides in class Board
? If the Board
were to validate the moves itself, no caller, and in particular no agent, could violate the rules of the game:
现在这听起来像是浪费了封装的机会:真的不应该允许代理人随意修改董事会,而应该限制在合法的行动中。Game
当更新的状态驻留在 class 中时,为什么 class 有责任决定什么是合法移动Board
?如果Board
要自行验证动作,则没有调用者,特别是没有代理,可以违反游戏规则:
public class Board {
// private fields with state
// public methods to query state
public void perform(Move move) throws IllegalMoveException;
}
回答by James
It's not always a sin to leave a field public, but you limit yourself severely in that you're coupling the implementation to the classes using it. Say later you want to add a listener that notifies you anytime the value in X is set. You have no way to do this without refactoring everything. If you implement setX() and hide the field itself, you will only have to change the implementation of setX() to notify you that a change was made with no change to classes utilizing this method.
将字段公开并不总是一种罪过,但您会严重限制自己,因为您将实现与使用它的类耦合。稍后说您想添加一个侦听器,它会在 X 中的值设置时通知您。如果不重构一切,你就无法做到这一点。如果您实现 setX() 并隐藏字段本身,则只需更改 setX() 的实现即可通知您进行了更改,而不会更改使用此方法的类。
回答by pm100
This is one advantage that c# has over Java. YOu can declare a class that has public fields and later on change your mind use a hidden field with getter and setter; but the callers syntax is the same
这是 C# 相对于 Java 的优势之一。您可以声明一个具有公共字段的类,然后改变主意使用带有 getter 和 setter 的隐藏字段;但调用者的语法是一样的
public class Point
{
public int X;
}
becomes
变成
public class Point
{
int m_x;
public int X {get {return m_x;} set {m_x = value;}
}
but the caller always does
但来电者总是这样做
Point p;
p.X = 12;
回答by Roman Goyenko
If you know the interface won't change, it's perfectly legal to have the variable public. The problem is as the programs get more complex you would need to change the access to the field, but modern IDEs make refactoring easy, so I would say go ahead.
如果您知道接口不会改变,那么将变量设为 public 是完全合法的。问题是随着程序变得越来越复杂,您需要更改对字段的访问,但现代 IDE 使重构变得容易,所以我会说继续。
回答by Dave Newton
Yes and no.
是和否。
No, if: you're certain of your scope, and don't need anything that expects normal Java properties (getters/setters), then it's likely not a big deal.
不,如果:您确定自己的范围,并且不需要任何需要普通 Java 属性(getter/setter)的东西,那么这可能没什么大不了的。
yes, if: some of that behavior may change, like you need to abstract a calculation affecting x or y without affecting calling code, or you're using something that expects getters and/or setters.
是的,如果:某些行为可能会改变,例如您需要抽象影响 x 或 y 的计算而不影响调用代码,或者您正在使用需要 getter 和/或 setter 的东西。
In general, it's easiest to follow the normal Java property pattern--it eliminates one type of risk. And it's one reason I wish Java had real properties.
一般来说,遵循正常的 Java 属性模式是最容易的——它消除了一种风险。这也是我希望 Java 具有真实属性的原因之一。
回答by Platinum Azure
Java's "rules" aren't absolute. In your case, when you're using objects only to store data and not providing critical behaviors, it is perfectly okay to leave the fields public.
Java 的“规则”不是绝对的。在您的情况下,当您仅使用对象来存储数据而不提供关键行为时,将字段公开是完全可以的。
On the other hand, for fields that you don't need or want to expose to the user, because they might only be relevant in an internal context, then you SHOULD mark the fields private and only provide getters and/or setters if you really need/want to.
另一方面,对于您不需要或不想向用户公开的字段,因为它们可能只在内部上下文中相关,那么您应该将字段标记为私有,并且只提供 getter 和/或 setter,如果您真的需要/想要。
The more complicated your object, the more important it is to ensure a consistent state between operations, and therefore the more likely the programmer will be to keep the object's state well encapsulated.
对象越复杂,确保操作之间的状态一致就越重要,因此程序员就越有可能保持对象的状态封装得很好。
回答by SetSlapShot
I see no problem with this. If a sin, it's at least not a mortal one. Vectors are another approach, but from what I've gathered vectors in java require too much overhead.
我认为这没有问题。如果是罪,至少不是致命的。矢量是另一种方法,但从我在 java 中收集的矢量来看,需要太多的开销。
回答by Mechkov
Public fields break the rule of encapsulation i.e protection of the data. Yes, they do hold data but by having your instance variable PUBLIC it can be accessed by any class out there in your workspace. Instance variables can be protected as well as private.Your getter and setter methods are used just so you can modify the data that your class's instance variables hold. Usually your setter methods would have some kind of validation which is why we have to protect the instance variables of corrupt data (by marking them private or protected)
公共字段打破了封装规则,即保护数据。是的,它们确实保存数据,但是通过让您的实例变量 PUBLIC 可以被工作区中的任何类访问。实例变量可以受到保护,也可以是私有的。使用 getter 和 setter 方法只是为了修改类的实例变量保存的数据。通常你的 setter 方法会有某种验证,这就是为什么我们必须保护损坏数据的实例变量(通过将它们标记为私有或受保护)
In the example above you have a constructor,which is initializing your inst. variable, but the chances are that you, as the developer of this class, has the rights and know what to data to insert in order to keep the integrity of your class. Someone else working on your class might not be aware of that and might access your variables by breaking the encapsulation and your program as a whole.
在上面的示例中,您有一个构造函数,它正在初始化您的 inst。变量,但作为该类的开发者,您很有可能拥有权利并知道要插入什么数据以保持类的完整性。在您的类上工作的其他人可能没有意识到这一点,并且可能通过破坏封装和整个程序来访问您的变量。
Consider x=-10; when x can only be from 0 to 100 (for example). I would recommend sticking to the encapsulation principles. Hope this helps!
考虑 x=-10; 当 x 只能是 0 到 100 时(例如)。我建议坚持封装原则。希望这可以帮助!
回答by nsayer
Sometimes the rules anticipate usage that you may not have considered.
有时,规则会预测您可能没有考虑过的用法。
What if you make a set or a hashmap of points? For that to work the way you likely desire it to work, you should implement equals() and hashcode() overloads, but then the best practice is to make the object immutable - that way you can't change the values out from under the set/map.
如果您制作一组点或散列图怎么办?为了让它按照您可能希望的方式工作,您应该实现 equals() 和 hashcode() 重载,但最佳实践是使对象不可变 - 这样您就无法从下面更改值设置/地图。