在Java中像对象一样构造
是否完全违反Java创建类结构对象的方式?
class SomeData1 { public int x; public int y; }
我可以看到一个类,其访问器和增变器更像Java。
class SomeData2 { int getX(); void setX(int x); int getY(); void setY(int y); private int x; private int y; }
第一个示例中的类在符号上很方便。
// a function in a class public int f(SomeData1 d) { return (3 * d.x) / d.y; }
这不是那么方便。
// a function in a class public int f(SomeData2 d) { return (3 * d.getX()) / d.getY(); }
解决方案
回答
如果我们知道它始终是一个简单的结构,并且我们永远也不想在其上添加行为,那么我看不出有什么害处。
回答
如果Java方法是OO方法,那么是的,使用公共字段创建类会破坏围绕信息隐藏的原则,即信息隐藏,即对象应管理自己的内部状态。 (因此,我不仅向我们吐了行话,信息隐藏的好处还在于,类的内部工作隐藏在接口后面,因为我们想更改用于保存struct类的字段之一的机制,我们可以可能需要返回并更改使用该类的所有类...)
我们也不能利用对JavaBean命名兼容类的支持,如果我们决定在使用表达式语言编写的JavaServer Page中使用该类,那么这会很麻烦。
我们可能还会对JavaWorld文章"为什么Getter和Setter方法是邪恶的"感兴趣,以考虑何时不实现访问器和mutator方法。
如果我们正在编写一个小型解决方案,并且希望减少所涉及的代码量,那么Java方法可能不是正确的方法,我想它总是取决于我们以及我们要解决的问题。
回答
这是一个经常讨论的话题。在对象中创建公共字段的缺点是我们无法控制为其设置的值。在有许多程序员使用同一代码的小组项目中,避免副作用很重要。此外,有时最好返回字段对象的副本或者以某种方式对其进行转换等。我们可以在测试中模拟此类方法。如果我们创建一个新类,则可能看不到所有可能的操作。就像有一天防御性编程一样,getter和setter可能会有所帮助,并且创建/使用它们不需要花费很多。因此它们有时很有用。
实际上,大多数字段都有简单的获取器和设置器。可能的解决方案如下所示:
public property String foo; a->Foo = b->Foo;
更新:极不可能在Java 7中或者可能在Java 7中添加属性支持。 Groovy,Scala等其他JVM语言现在确实支持此功能。亚历克斯·米勒
回答
这是关于面向对象设计的问题,而不是Java语言。通常,将数据类型隐藏在类中并仅公开属于类API的方法是一种很好的做法。如果公开内部数据类型,则以后将永远无法更改它们。如果隐藏它们,则对用户的唯一义务是方法的返回值和参数类型。
回答
顺便说一句,我们作为示例给出的结构已经在Java基类库中以java.awt.Point
的形式存在。它具有x和y作为公共字段,请自行检查。
如果我们知道自己在做什么,并且团队中的其他人都知道,那么可以使用公共场所。但是我们不应该依赖它,因为它们会引起头痛,就像与开发人员使用对象有关的错误一样,就像它们是堆栈分配的结构一样(Java对象始终作为引用而不是副本发送给方法)。
回答
确实使用常识。如果我们有类似的东西:
public class ScreenCoord2D{ public int x; public int y; }
然后,将它们包装在吸气剂和吸气剂中毫无意义。我们将永远不会以其他任何方式将x,y坐标存储在整个像素中。 Getter和Setter只会让我们放慢速度。
另一方面,具有:
public class BankAccount{ public int balance; }
我们可能需要更改将来某个时候的余额计算方式。这实际上应该使用getter和setter。
总是最好知道为什么要应用良好实践,以便知道何时可以改变规则。
回答
回复:阿库,伊兹布,约翰·托普利...
当心可变性问题...
忽略getter / setter似乎是明智的。在某些情况下,实际上可能没问题。这里显示的建议模式的真正问题是可变性。
问题是,一旦我们将包含非最终公共字段的对象引用传递出去。带有该参考的其他任何内容都可以自由修改这些字段。我们不再可以控制该对象的状态。 (想想如果Strings是可变的,会发生什么。)
当该对象是另一个对象的内部状态的重要组成部分时,情况就变得很糟,我们刚刚公开了内部实现。为避免这种情况,必须返回该对象的副本。这可行,但可能会产生大量的一次性使用副本,从而给GC造成巨大压力。
如果我们有公共字段,请考虑使该类为只读。将字段作为参数添加到构造函数中,并将字段标记为final。否则,请确保我们没有暴露内部状态,并且如果我们需要为返回值构造新的实例,请确保不会过度调用它。
参见:Joshua Bloch撰写的" Effective Java"-项目#13:支持不变性。
PS:还请记住,目前,所有JVM都将尽可能优化getMethod,从而仅产生一条字段读取指令。
回答
为了解决可变性问题,我们可以将x和y声明为final。例如:
class Data { public final int x; public final int y; public Data( int x, int y){ this.x = x; this.y = y; } }
尝试写入这些字段的调用代码将得到一个编译时错误"字段x被声明为final;无法分配"。
然后,客户端代码可以具有我们在帖子中描述的"捷径"便利
public class DataTest { public DataTest() { Data data1 = new Data(1, 5); Data data2 = new Data(2, 4); System.out.println(f(data1)); System.out.println(f(data2)); } public int f(Data d) { return (3 * d.x) / d.y; } public static void main(String[] args) { DataTest dataTest = new DataTest(); } }
回答
如果以后改变主意,则所有现有的调用者都将断开,使用公共字段访问的问题与使用new而不是使用工厂方法的问题相同。因此,从API演变的角度来看,通常最好是硬着头皮并使用getters / setter方法。
我走另一条路的地方是,当我们强烈控制对类的访问时,例如,在用作内部数据结构的内部静态类中。在这种情况下,使用字段访问可能会更加清晰。
顺便说一句,按照e-bartek的主张,IMO在Java 7中添加属性支持的可能性很小。
回答
在构建私有内部类以简化代码时,我经常使用此模式,但是我不建议在公共API中公开此类对象。通常,使公共API中的对象不可变的频率越高越好,并且不可能以不可变的方式构造"类似结构的"对象。
顺便说一句,即使我将这个对象编写为私有内部类,我仍然会提供一个构造函数来简化初始化对象的代码。只需三行代码即可获得一个可用的对象,这简直是一团糟。
回答
我们可以使用Java创建没有公共字段的简单类,但没有方法,但是它仍然是一个类,并且在语法和内存分配方面仍然像类一样进行处理。没有办法在Java中真正复制结构。
回答
当我需要从一个方法返回多个值时,有时会使用此类。当然,这种物体寿命短,可见性非常有限,因此应该可以。
回答
我已经在几个项目中尝试过这种方法,其理论是吸气剂和设置剂会在语义上毫无意义的混乱中使代码混乱,并且其他语言似乎对基于约定的数据隐藏或者职责划分(例如python)也很好。
正如其他人在上面指出的那样,我们遇到了2个问题,它们并不是真正可以解决的问题:
- Java世界中几乎所有自动化工具都依赖于getter / setter约定。同上,其他人也提到过,jsp标记,spring配置,eclipse工具等。。。与工具期望看到的东西作斗争是长时间讨论Google的秘诀,试图寻找这种非标准的启动方式春豆。真的不值得麻烦。
- 一旦应用程序具有数百个公共变量的精美代码,我们可能会发现至少一种情况,它们不足-我们绝对需要不变性,或者需要在设置变量时触发一些事件,或者我们想抛出变量更改的异常,因为它将对象状态设置为令人不愉快的状态。然后,我们会陷入难以置信的选择之间,即在直接引用变量的任何地方都使用一些特殊的方法来使代码混乱,在应用程序中有1000种变量中的3种具有特殊的访问形式。
最好的情况是完全在一个独立的私人项目中工作。一旦将整个内容导出到可公开访问的库中,这些问题就会变得更大。
Java非常冗长,这是一件很诱人的事情。不要这样
回答
只要作者知道它们是结构(或者数据穿梭)而不是对象,该类型的代码就没有问题。许多Java开发人员无法分辨格式正确的对象(不仅仅是java.lang.Object的子类,而是特定领域中的真实对象)与菠萝之间的区别。因此,他们最终在需要对象时编写结构,反之亦然。
回答
与大多数事情一样,有一般规则,然后有特定情况。
如果我们正在做一个封闭的,捕获的应用程序,以便知道如何使用给定的对象,那么我们可以行使更多的自由度来提高可见性和/或者效率。
如果我们正在开发一个将由我们无法控制的其他人公开使用的类,则倾向于使用getter / setter模型。
与所有事物一样,只是使用常识即可。
通常可以与公众进行一轮初赛,然后稍后再将其更改为吸气剂。