Java方法命名规范:太多的getter方法

40
为什么Java方法名如此频繁地使用“get”前缀?在我的Java程序中,至少有很多以单词“get”开头的方法。获取方法的百分比非常高,我开始觉得由于通货膨胀,“get”这个词失去了意义。它是我代码中的噪音。
我注意到在函数式/声明式编程和PL/SQL中使用了不同的命名约定。方法名称仅说明方法返回的内容。而不是像account.getAmount()Time.getIsoFormattedDateString(Date date)那样,他们将使用account.amount()Time.isoFormattedDateString(Date date)。对我来说,这是完全有意义的,因为函数名称描述了评估方法的结果(假设没有副作用,无论如何都不应该有)。 “get”前缀似乎是多余的。
我刚开始阅读《Clean Code》一书。它说方法应该只做一件事情,而且那件事通常应该是以下三种之一:
1. 通知某个对象发生了事件,通常将事件作为参数传递。 2. 关于某个对象提出问题,通常使用方法名称形成自然语言语句,将对象作为参数传递并返回布尔值。 3. 获取某些东西,可能会将一些查找键或要转换的对象作为参数传递,并始终返回所需的对象/值。
我的问题是关于第三类。除了“get”之外,还有命名约定适用于这种方法吗?选择方法名称/前缀时使用什么标准?
以下是一个例子:
我有一个类,其中包含两个方法getDates()getSpecialDates()getDates()只是返回一个私有变量(日期集合的引用)的值。据我理解,这是标准的getter。 getSpecialDates()不同;它调用getDates(),从另一个类中获取过滤器,应用该过滤器并返回实际上是getDates()的子集。

方法getSpecialDates()可以被命名为computeSpecialDates()findSpecialDates()selectSpecialDates()elicitSpecialDates()等。或者我可以直接将其命名为specialDates()。然后,为了保持一致性,我可以将getDates()重命名为dates()

为什么要区分应该添加"get"前缀和不需要添加的方法,为什么要找替代词来代替"get"?


6
只要每个“get”确实是“获取”,使用“getThis”和“getThat”并不会失去它的意义。这确实有助于知道你正在做什么。就像编译器不会说“哦,又一个get?现在你只是在说话而已…” - corsiKa
20个回答

27

就我个人而言,只要可能(比如说我不使用需要它的任何框架,例如Struts),我就不使用getter和setter。

我更喜欢在可能时编写不可变对象(public final字段),否则我只使用public字段:更少的样板代码,更高的生产力,更少的副作用。 get/set的原始理由是封装(使您的对象尽可能害羞),但实际上,我并不经常需要它。

在《Effective Java》中,Joshua Bloch做出了这个有说服力的建议:

类应该是不可变的,除非有很好的理由使它们可变......如果一个类不能被设为不可变,那么尽量减少它的可变性。

在同一本书中,他还说(但我不想在这里复制整本书):

JavaBeans模式具有严重的缺点。

我完全赞同这一点,因为JavaBeans最初只适用于一个非常狭窄的问题域:在IDE中操作图形组件。使用为解决另一个问题而设计的解决方案是一种不好的做法。


4
我同意。其中我最不喜欢的一件事情是:有人会说太多的公共数据会使API变得复杂,我同意这种说法。但接着他又说解决方案是将它们设为私有,并创建getter和setter方法。这有什么帮助呢?为什么直接使用“x=myObject.someValue”会有问题,而使用“x=myObject.getSomeValue()”就能消除所有问题呢?我唯一看到使用getter和setter方法的好处是当存在副作用时。 - Jay
@Jay:当getSomeValue()返回一个对象时,情况会更糟,因为有些人没有意识到仅通过getter就可以更改对象的状态。 - Justin Ardini
14
我同意“更喜欢不可变对象”的观点,但我不同意有关JavaBeans的结论。第二个引用是关于为构造函数调用一堆setter方法还是使用建造者模式的问题 - 而不是关于一般情况下使用JavaBeans的问题。JavaBeans只是一个组件规范。不需要图形界面或IDE - BeanBox不是JavaBeans的目的;它只是演示其功能的简单方式。如果“使用为解决另一个问题而设计的解决方案是不好的做法” - 那么就别再用Java开发Web应用程序了 - Oak是为电视机顶盒而设计的。 :P - Nate
@Ardini: 绝对没错!函数名称的一般问题不仅不完整而且是彻头彻尾的谎言!我曾经编写了一个名为“validateStocknumber”的函数,它检查股票号码是否有效并返回true或false。另一个程序员过来把它改成还更新了股票记录,但没有改变名称。然后另外一个人再次更改它,以至于现在它甚至不能验证股票号码了!天啊! - Jay
2
我一直认为访问器方法的动机是强制对值进行约束。例如,您无法阻止客户端将public int字段设置为负数,但是您的setter可以实现这一点。 - brianmearns
显示剩余2条评论

16

9
如果Java能够添加真正的属性,我们就不必再担心这个问题了。我对它们被从Java 7的提案中删除感到失望。 - Powerlord
1
@R. Bemrose:我同意,我希望他们能添加属性。但是,我们仍然需要担心所有遗留代码和现有库/框架不会消失。此外,人们改变习惯通常很困难。似乎有很多人使用get____,无论该方法是否可以被视为属性。 - TM.
1
库和框架不需要更改,因为任何关于一流属性支持的提案都很可能是语法糖,并在类中生成实际方法。工具必须进行调整(例如代码生成器、IDE等)。 - Matt

9

Java中有很多get*方法的原因之一是Java不支持像.net/COM那样的“属性”,因此Java bean等使用函数getX和setX来复制名为X的属性的功能。一些针对Java的IDE利用这一点允许设置和检索属性。


Naked Objects 是一个依赖于命名约定来生成前端的框架。+1 - APC
1
实际上(作为Naked Objects的贡献者),这是一个可插拔的功能。我们使用FacetFactorys构建元模型,而默认工厂会查找“get”前缀。但他们不需要这样做。 这种设计还允许我们支持其他语言;我几周前使用Groovy使NO工作正常,而且(碰巧)本周已经开始了Scala的工作。这使用了Scala的@BeanProperty注释。 但是,对于OP来说,我们认为getter也没有什么问题:它们是一种表示“知道什么”责任的命名约定。 -- Dan - Dan Haywood

6

Java中经常编写getter和setter方法的原因之一是使用JavaBeans约定。

然而,标准Java API本身并不一致。例如,类String有一个length()方法,而接口Collection定义了一个size()方法,而不是getLength()getSize()

Java不支持统一访问原则,因此您必须编写getter和setter方法来访问属性。


“Java不支持统一访问原则”是什么意思?get()方法可能会返回计算出的值,而不是私有变量的值——实例变量与同名的get()或set()方法并没有内在联系。 - Tarski
@Tarski 你了解UAP吗?它意味着你可以像访问成员变量一样访问属性。所以类似Foo.thingy的东西实际上会在幕后调用Foo.getThingy()Foo.thingy看起来好像是直接访问成员变量,但实际上并不是。像C#、Ruby、Scala这样的语言支持这种特性。 - Jesper
JavaBeans规范要求为每个字段提供get/set方法,但是lenghtsize很可能不是字段。实际上,它们似乎是基于字段计算出来的值。 - Paolo Fulgoni
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Gunnar Bernstein

6

getSpecialDates()computeSpecialDates()findSpecialDates()selectSpecialDates()elicitSpecialDates()这样的方法名对我来说是命令,因为它们在名称中使用了动词(动作)。命令每次调用时都会产生副作用。而像date()dates()specialDates() [名词]这样的方法是返回有用值且没有副作用的方法。多次调用该方法每次都会返回相同的值,除非调用了一个具有改变状态副作用的命令。


5

4

使用常见的get/set约定是需要Java开发人员遵守的原因之一,因为许多框架依赖于它来创建bean并设置字段。例如,如果您为Spring bean配置了一些属性,如<property name="foo" value="bar" />,而类中没有名为setFoo()的方法,则创建bean时会出现错误。


4
前提1:一个方法应该只做一件事。前提2:一个getter方法,无论它是否使用get前缀,都不应该有副作用。鉴于这两个前提,我建议:一个作用是获取某些东西并且以相对简单和廉价的方式实现的方法不需要在其名称中包含动词。
getter的存在理由不是为了执行某些操作,而是为了计算出某些东西。我们不关心方法所做的事情。由于它没有副作用,方法中发生的任何计算都不会引起兴趣。我们只关心方法返回的内容。方法名称应该以名词的形式反映出来。仅由名词组成的方法名称应始终是“getter”。
通过缺乏动词可以推断出前缀“get”中的信息。这比使用get前缀更简单直观。
一个方法名称仅包含名词且具有返回值可以假定没有副作用并且相对便宜。一个方法名称包含动词但没有返回值的存在是为了产生副作用。方法名称包含动词并且具有返回值可以假定相对昂贵并且可能具有副作用。
似乎每个人都在到处写“get”的原因仅仅是源自JavaBeans模式的教条传统。在实际需要时才使用get前缀吧!

3

正如许多人已经提到的那样,get()和set()是Java Beans约定的一部分。这对于与Java规范的其他部分进行互操作是必要的。例如,在JSP中,您可以通过指定属性名称而不使用 get 前缀来访问Java bean的成员。

给定豆子:

public class Foo {
  public int getX() { return 1; }
}

我们可以通过以下JSP获取X:-
<jsp:useBean id="aFoo" class="Foo" />
<c:out value="${aFoo.X}" />

除了 "get" 之外,这种方法还有其他命名惯例吗?
是的,对于布尔属性,您可以使用"is"而不是"get"。

3

个人而言,我痴迷于“获取”(get)。这只是人类的语言。当你想要某些东西时,你想要“获取”(get)某些东西。在前缀中使用“获取”(get)没有任何问题。关于命名规范,我可以考虑数据库查询的“选择”(Select)前缀——例如,SelectUsers


1
正如我在问题中所说的那样,get前缀似乎存在问题的地方在于它经常显得不必要,只是噪音,因此应该省略。 - Are Husby
3
那只是你的观点,而不是事实。 - duffymo
1
双手加一 :) 如果你的类中有100个方法(或通过继承可用),那么只需键入“yourObjectInstance.get”并等待一会儿,就可以强制IDE中的自动完成过滤所有“get”方法。如果它们不以get开头,那么找到正确的方法将会更加困难。这种混淆的典型例子是collection.size()和array.length()。 - m_pGladiator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接