Effective Java 第三版——68. 遵守普遍接受的命名约定

Tips
书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code
注意,书中的有些代码里方法是基于Java 9 API中的,所以JDK 最好下载 JDK 9以上的版本。

Effective Java, Third Edition

  1. 遵守普遍接受的命名约定

Java平台有一组完善的命名约定(naming conventions),其中许多约定包含在Java语言规范[JLS, 6.1]中。宽泛地说,命名约定分为两类:字面(typographical)的和语法的(grammatical)。

只有少量的字面的命名约定,包括包、类、接口、方法、属性和类型变量。你不应该违反它们,而且没有理由去违反。如果API违反了这些约定,那么它可能很难使用。如果实现违反了这些规则,可能很难维护。在这两种情况下,违反约定都有可能使其他使用代码的程序员感到困惑和恼怒,并可能导致他们做出错误的假设,从而导致错误。本条目概述了各个命名约定。

包和模块名称应该是分层的,每个部分以句点分隔。 每个部分应包含小写字母字符,很少包含数字。任何在你的组织外部使用的包的名称都应该以你的组织的Internet域名开头,但包名正好相反,例如,edu.cmu,com.google,org.eff。 名称以java和javax开头的标准类库和可选包是此规则的例外。 用户不得创建名称以java或javax开头的包或模块。 可以在JLS [JLS, 6.1]中找到将Internet域名转换为包名称前缀的详细规则。

包名的其余部分应该由描述包的一个或多个组件构成。组件应该很短,通常为8个或更少的字符。鼓励使用有意义的缩写,例如util而不是utilities。缩写词是可以接受的,例如awt。组件通常应该由一个单词或缩写组成。

除了Internet域名之外,许多包的名称只包含一个组件。 其他组件适用于大型设施,其大小要求将其分解为非正式层次结构。 例如,javax.util包具有丰富的包层次结构,其名称如java.util.concurrent.atomic。 这样的包被称为子包,尽管几乎没有语言对包层次结构提供支持。

类和接口名称(包括枚举和注解类型名称)应由一个或多个单词组成,每个单词的首字母大写,例如List或FutureTask。 除了首字母缩略词和某些常用缩写(如max和min)之外,应避免使用缩写。 关于首字母缩略词是大写还是仅首字母大写,存在一些分歧。 虽然一些程序员仍然使用大写字母,但是可以做出强有力的论证,只支持大写第一个字母:即使多个首字母缩写连续出现,仍然可以知道一个单词从哪里开始,下一个单词从哪里结束。 你更喜欢看哪个类名,HTTPURL或HttpUrl?

方法和属性名遵循与类和接口名相同的字面约定,除了方法或属性名的第一个字母应该是小写,例如remove或ensureCapacity。 如果首字母缩略词作为方法或属性名称的第一个单词出现,则它应该是小写的。

前面规则的唯一例外是“常量属性”,它的名称应该由一个或多个大写单词组成,由下划线分隔,例如VALUES或NEGATIVE_INFINITY。常量属性是一个静态的final属性,其值是不可变的。如果静态final属性具有基本类型或不可变引用类型(条目 17),那么它就是常量属性。例如,枚举常量是常量属性。如果静态final属性有一个可变的引用类型,那么如果所引用的对象是不可变的,那么它仍然可以是一个常量属性。注意,常量属性是唯一推荐的下划线用法。

局部变量名称与成员名称具有相似的字面命名约定,但允许使用缩写除外,单个字符和短字符序列的含义取决于它们出现的上下文,例如i,denom,houseNum。 输入参数是一种特殊的局部变量。 它们的名称应该比普通的局部变量更加仔细,因为它们的名称是其方法文档中不可或缺的一部分。

类型参数名通常由单个字母组成。最常见的是以下五种类型之一:T表示任意类型,E表示集合的元素类型,K和V表示映射的键和值类型,X表示异常。方法的返回类型通常为R。任意类型的序列可以是T、U、V或T1、T2、T3。

为了快速参考,下表列出了字面约定的示例。

标识符类型 示例
包名或模块 org.junit.jupiter.api, com.google.common.collect
类或接口 Stream, FutureTask, LinkedHashMap, HttpClient
方法或属性 remove, groupingBy, getCrc
常量属性 MIN_VALUE, NEGATIVE_INFINITY
局部变量 i, denom, houseNum
类型参数 T, E, K, V, X, R, U, V, T1, T2

语法命名约定比字面约定更灵活,也更有争议。包没有语法命名约定。可实例化的类,包括枚举类型,通常使用一个或多个名词短语来命名,例如Thread、PriorityQueue或ChessPiece。不可实例化的实用程序类(条目 4)通常使用复数名词来命名,例如Collector或Collections。接口的名称类似于类,例如Collection或Comparator,或者以able或ible结尾的形容词,例如Runnable、Iterable或Accessible。因为注解类型有如此多的用途,所以没有哪部分词性占主导地位。名词、动词、介词和形容词都很常见,例如,BindingAnnotation、Inject、ImplementedBy或Singleton。

执行某些操作的方法通常使用动词或动词短语(包括对象)命名,例如append或drawImage。 返回boolean类型的方法通常具有以单词is,或不太常用的has开头的名称,后跟名词,名词短语或任何用作形容词的单词或短语,例如isDigit,isProbablePrime,isEmpty, isEnabled,或hasSiblings。

方法返回被调用对象的非boolean的方法或属性,通常使用以get开头的名词、名词短语或动词短语来命名,例如size、hashCode或getTime。有一种说法是,只有第三种形式(以get开头)才是可接受的,但这种说法几乎没有根据。前两种形式的代码通常可读性更强,例如:

if (car.speed() > 2 * SPEED_LIMIT)
    generateAudibleAlert("Watch out for cops!");

以get开头的形式起源于基本过时的Java bean规范,该规范构成了早期可重用组件体系结构的基础。有一些现代工具继续依赖于Beans命名约定,你可以随意在任何与这些工具结合使用的代码中使用它。如果类同时包含相同属性的setter和getter,则遵循这种命名约定也有很好的先例。在本例中,这两个方法通常被命名为getAttribute和setAttribute。

一些方法名称值得特别提及。 转换对象类型,返回不同类型的独立对象的实例方法通常称为toType,例如toString或toArray。 返回类型与接收对象类型不同的视图(条目 6)的方法通常称为asType,例如asList。 返回与调用它们的对象具有相同值的基本类型的方法通常称为typeValue,例如intValue。 静态工厂的常用名称包括from,of,valueOf,instance,getInstance,newInstance,getType和newType(条目 1,第9页)。

属性名称的语法约定不太完善,并且不如类,接口和方法名称那么重要,因为设计良好的API包含很少的暴露属性。 boolean类型的属性通常被命名为boolean 访问器方法,省略了初始的is前缀,例如,initialized,composite。 其他类型的属性通常以名词或名词短语命名,例如height,digits或bodyStyle。 局部变量的语法约定类似于属性,但甚至更弱。

总之,将标准命名约定内在化,并将其作为第二天性来使用。字面约定是直接的,而且在很大程度上是明确的;语法约定更加复杂和松散。引用Java语言规范[JLS, 6.1]中的话说,“如果长期以来的传统用法要求不遵循这些约定,就不应该盲目地遵循这些约定”。使用常识。

posted @ 2019-03-27 11:00  林本托  阅读(578)  评论(0编辑  收藏  举报