静态最终变量的Java命名约定

Ale*_*sey 89 java coding-style

有一条规则说:

表示常量(最终变量)的名称必须全部使用下划线来分隔单词(取自http://geosoft.no/development/javastyle.html)

这适用于原始类型,如int或字符串:

private static final int MAX_COUNT = 10;
Run Code Online (Sandbox Code Playgroud)

但是什么是非原始类型?在大多数情况下,我见过以下内容:

private static final Logger log = Logger.getLogger(MyClass.class);
Run Code Online (Sandbox Code Playgroud)

或者在单例中,实例变量不是大写的.

问题是声明这些类型的变量(如日志和实例)的正确方法是什么?

mre*_*mre 68

这仍然是一个常数.有关常量命名约定的更多信息,请参阅JLS.但实际上,这只是一个偏好问题.


接口类型中的常量名称应该是,并且final 类类型的变量通常可以是一个或多个单词,首字母缩略词或缩写的序列,全部为大写,其中组件由下划线"_"字符分隔.常量名称应该是描述性的,而不是不必要的缩写.通常,它们可以是任何适当的词性.常量名的例子包括MIN_VALUE,MAX_VALUE,MIN_RADIX,和MAX_RADIX之类的Character.

一组常量表示一组中的替代值,或者不太常见的是整数值中的掩码位,有时会使用常用首字母缩写作为名称前缀来指定,如下所示:

interface ProcessStates {
  int PS_RUNNING = 0;
  int PS_SUSPENDED = 1;
}
Run Code Online (Sandbox Code Playgroud)

涉及常量名称的模糊很少见:

  • 常量名称通常没有小写字母,因此它们通常不会模糊包或类型的名称,也不会影响通常包含至少一个小写字母的字段.
  • 常量名称不能掩盖方法名称,因为它们在语法上是有区别的.

  • "......班级类型***的最终变量可能通常是......" (8认同)
  • UPPER_CASE 约定是否适用于 `final` **non**-`static` *local* 变量,例如 `void method() { final int VALUE = 5; /* ... */ }`? (3认同)

cru*_*ush 41

关于这一点的对话似乎是关于命名interfaceabstract类的对话的对立面.我发现这令人担忧,并认为这个决定比简单地选择一个命名约定并且总是使用它更加深入static final.

摘要和界面

在命名接口和抽象类时,接受的约定已经演变为不为您abstract class或者interface使用任何标识信息添加前缀或后缀,以表明它不是类.

public interface Reader {}
public abstract class FileReader implements Reader {}
public class XmlFileReader extends FileReader {}
Run Code Online (Sandbox Code Playgroud)

据说开发人员不需要知道上面的类是abstract或者是interface.

静态决赛

我个人的偏好和信念是,在引用static final变量时我们应该遵循类似的逻辑.相反,我们在确定如何命名时评估其用法.似乎所有大写的论证都是从C和C++语言中有些盲目采用的.据我估计,这不是继续Java传统的理由.

意向问题

我们应该问自己,static final在我们自己的背景下,我们的功能是什么.以下是static final不同情况下如何使用的三个示例:

public class ChatMessage {
    //Used like a private variable
    private static final Logger logger = LoggerFactory.getLogger(XmlFileReader.class);

    //Used like an Enum
    public class Error {
        public static final int Success = 0;
        public static final int TooLong = 1;
        public static final int IllegalCharacters = 2;
    }

    //Used to define some static, constant, publicly visible property
    public static final int MAX_SIZE = Integer.MAX_VALUE;
}
Run Code Online (Sandbox Code Playgroud)

你能在所有三种场景中使用全部大写吗?当然,但我认为可以说它会削弱每个人的目的.那么,让我们分别检查每个案例.


目的:私人变量

Logger上面的示例中,记录器被声明为private,并且只在类中使用,或者可能在内部类中使用.即使它是在声明protected package可见性,它的用法是相同的:

public void send(final String message) {
    logger.info("Sending the following message: '" + message + "'.");
    //Send the message
}
Run Code Online (Sandbox Code Playgroud)

在这里,我们不关心logger是一个static final成员变量.它可以只是一个final实例变量.我们不知道.我们不需要知道.我们需要知道的是,我们将消息记录到类实例提供的记录器中.

public class ChatMessage {
    private final Logger logger = LoggerFactory.getLogger(getClass());
}
Run Code Online (Sandbox Code Playgroud)

LOGGER在这种情况下你不会给它命名,那么为什么要把它全部命名为大写static final呢?它的背景或意图在两种情况下都是相同的.

注意:我改变了我对package可见性的立场,因为它更像是一种public访问形式,仅限于package关卡.


目的:枚举

现在您可能会说,为什么使用static final整数作为enum?这是一个仍在不断发展讨论,我甚至会说半争议,所以我不会试图通过冒险来彻底破坏这个讨论.但是,建议您可以实现以下接受的枚举模式:

public enum Error {
    Success(0),
    TooLong(1),
    IllegalCharacters(2);

    private final int value;

    private Error(final int value) {
        this.value = value;
    }

    public int value() {
        return value;
    }

    public static Error fromValue(final int value) {
        switch (value) {
        case 0:
            return Error.Success;
        case 1:
            return Error.TooLong;
        case 2:
            return Error.IllegalCharacters;
        default:
            throw new IllegalArgumentException("Unknown Error value.");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的变体实现了允许显式转换enum->int和的相同目的int->enum.在通过网络传输此信息的范围内,本机Java序列化过于冗长.一个简单的int,short或者byte可以节省巨大的带宽.我可以深入一个长篇大论比较和有关对比优劣enumVS static final int涉及类型的安全性,可读性,可维护性等; 幸运的是,这不属于本次讨论的范围.

底线是这样,有时static final int会用作enum样式结构.

如果您能够接受上述陈述是正确的,我们可以通过讨论风格来跟进.声明时enum,接受的样式表示我们不执行以下操作:

public enum Error {
    SUCCESS(0),
    TOOLONG(1),
    ILLEGALCHARACTERS(2);
}
Run Code Online (Sandbox Code Playgroud)

相反,我们执行以下操作:

public enum Error {
    Success(0),
    TooLong(1),
    IllegalCharacters(2);
}
Run Code Online (Sandbox Code Playgroud)

如果你static final的整数块是松散的enum,那你为什么要使用不同的命名约定呢?它的背景或意图在两种情况下都是相同的.


目的:静态,恒定,公共财产

这个用例可能是最混乱和最有争议的.静态常量大小用法示例是最常遇到的情况.Java 不再需要sizeof(),但有时候知道数据结构将占用多少字节很重要.

例如,考虑您正在向二进制文件写入或读取数据结构列表,并且该二进制文件的格式要求在实际数据之前插入数据块的总大小.这很常见,以便读者知道数据在场景中何时停止,后面有更多不相关的数据.考虑以下组成的文件格式:

File Format: MyFormat (MYFM) for example purposes only
[int filetype: MYFM]
[int version: 0] //0 - Version of MyFormat file format
[int dataSize: 325] //The data section occupies the next 325 bytes
[int checksumSize: 400] //The checksum section occupies 400 bytes after the data section (16 bytes each)
[byte[] data]
[byte[] checksum]
Run Code Online (Sandbox Code Playgroud)

此文件包含MyObject序列化为字节流并写入此文件的对象列表.这个文件有325个字节的MyObject对象,但是如果不知道每个对象的大小,MyObject你就无法知道每个字节属于哪个字节MyObject.所以,你定义MyObjecton 的大小MyObject:

public class MyObject {
    private final long id; //It has a 64bit identifier (+8 bytes)
    private final int value; //It has a 32bit integer value (+4 bytes)
    private final boolean special; //Is it special? (+1 byte)

    public static final int SIZE = 13; //8 + 4 + 1 = 13 bytes
}
Run Code Online (Sandbox Code Playgroud)

MyObject当写入如上定义的文件时,数据结构将占用13个字节.知道了这一点,在阅读我们的二进制文件时,我们可以动态地计算MyObject出文件中有多少个对象:

int dataSize = buffer.getInt();
int totalObjects = dataSize / MyObject.SIZE;
Run Code Online (Sandbox Code Playgroud)

这似乎是所有大写static final常量的典型用例和参数,我同意在这种情况下,所有大写都有意义.原因如下:

Java没有struct类似C语言的类,但是a struct只是一个包含所有公共成员而没有构造函数的类.这只是一个数据struct.所以,你可以声明一个classstruct类似方式:

public class MyFile {
    public static final int MYFM = 0x4D59464D; //'MYFM' another use of all uppercase!

    //The struct
    public static class MyFileHeader {
        public int fileType = MYFM;
        public int version = 0;
        public int dataSize = 0;
        public int checksumSize = 0;
    }
}
Run Code Online (Sandbox Code Playgroud)

让我以这个例子开头,说明我个人不会以这种方式解析.我建议使用一个不可变类,通过接受一个ByteBuffer或全部4个变量作为构造函数参数来内部处理解析.也就是说,访问(在这种情况下设置)这个struct成员看起来像:

MyFileHeader header = new MyFileHeader();
header.fileType     = buffer.getInt();
header.version      = buffer.getInt();
header.dataSize     = buffer.getInt();
header.checksumSize = buffer.getInt();
Run Code Online (Sandbox Code Playgroud)

这些不是static或者final,但它们是可以直接设置的公开公开的成员.出于这个原因,我认为当一个static final成员被公开曝光时,完全取消它是有意义的.这是一个时间,当它从公共的,非静态变量的区别很重要.

注意:即使在这种情况下,如果开发人员试图设置final变量,也会遇到IDE或编译器错误.


摘要

总之,您为static final变量选择的约定将是您的偏好,但我坚信使用环境应该严重影响您的设计决策.我个人的建议是遵循以下两种方法之一:

方法1:评估背景和意图 [highly subjective; logical]

  • 如果它是一个private应该与private实例变量无法区分的变量,那么将它们命名为相同.全是小写的
  • 如果它的目的是作为一种宽松enumstatic价值块,那么就像你一样命名enum.pascal case:每个单词的首字母
  • 如果它的目的是定义一些可公开访问的,常量的和静态的属性,那么让它全部大写就能脱颖而出

方法2:私人与公共 [objective; logical]

方法论2基本上将其背景浓缩为可见性,并且没有留下解释的余地​​.

  • 如果是private,protected那么它应该全部小写.
  • 如果是public,package那么它应该全部大写.

结论

这就是我查看static final变量命名约定的方法.我不认为它可以或应该被装入一个单一的捕获.我相信你应该在决定如何命名之前评估它的意图.

但是,主要目标应该是在整个项目/包的范围内保持一致.最后,这是你可以控制的全部.

(我确实希望遇到阻力,但也希望得到社区对这种方法的一些支持.无论你的立场如何,请在谴责,批评或赞美这种风格选择时保持文明.)

  • 啊,是的,通常的怯懦; 投票没有解释.我想当有人提出一些可能被认为违背常规的事情,或者更糟糕的是,但是阅读时间太长时,这是可以预期的. (4认同)
  • Downvoting原因并非随处可见. (2认同)
  • @baba 之所以被低估是因为没有遵循现代开发环境已经过时的盲目约定?很好的逻辑。 (2认同)
  • @crush,常量总是大写,无论它们的类型和可见性如何,无论它们是否是原始的.它是如何以及它应该如何由语言规范指定.IDE与它无关.记录器不是常量,因为它们具有功能,因此它们不应该被手工化.看,简单,最重要的是,符合标准. (2认同)
  • @NikolaYovchev 为什么需要通过命名约定知道变量是常量?仅仅因为标准如此规定并不是不发展的充分理由。显然,我知道标准的内容,并选择提供偏差,因为我们已经超越了需要名称来检查变量特征的时代。 (2认同)
  • 尝试在运行时修改静态最终记录器,看看它是如何实现的.你忽略了我的问题. (2认同)

Hen*_*olm 11

语言不关心.重要的是遵循您正在进行的项目的既定风格和惯例,以便其他维护者(或从现在起五个月后)尽可能不被混淆.

我认为可变对象的全大写名称肯定会让我感到困惑,即使对该对象的引用恰好存储在static final变量中.


Kas*_*zar 7

那是一个非常有趣的问题.我会根据他们的类型划分你问题中的两个常数.int MAX_COUNT是原始类型的常量,而Logger log是非原始类型.

当我们使用基本类型的常量时,我​​们只在代码中改变常量一次,public static final in MAX_COUNT = 10而我们只是在其他地方访问常量的值for(int i = 0; i<MAX_COUNT; i++).这就是我们对使用此约定感到满意的原因.

虽然在非原始类型的情况下,虽然我们只在一个地方初始化常量private static final Logger log = Logger.getLogger(MyClass.class);,但我们期望在其他地方变异或调用此常量的方法log.debug("Problem").我们不喜欢在大写字符之后添加点运算符.毕竟我们必须在点运算符之后放置一个函数名,这肯定会成为驼峰式名称.这就是为什么LOG.debug("Problem")看起来很尴尬.

String类型的情况也是如此.我们通常不会String在代码中对常量进行变异或调用方法,这就是我们为String类型对象使用大写命名约定的原因.


Edu*_*rdo 6

对对象的常量引用不是常量,而只是对对象的常量引用。

private static final不是什么将某事物定义为常数。这只是Java定义常量的方式,但这并不意味着每个private static final声明都放在其中以定义常量。

当我写的时候,private static final Logger我并不想定义一个常量,我只是想定义一个对象的引用,该对象是private(它不能从其他类访问),static(它是一个类级变量,不需要实例) )和final(只能分配一次)。如果它恰好与Java期望您声明一个常量的方式相符,那么运气不好,但这并不能使其成为常量。我不在乎编译器,声纳或任何Java专家怎么说。常量值就像MILLISECONDS_IN_A_SECOND = 1000是一回事,而对对象的常量引用是另一回事。

众所周知,黄金会发光,但并非所有发光的东西都是黄金。

  • 常量的旧概念已经丢失。用于指向实际固定值的常数。如今,您对可以更改其状态的对象具有不变的引用。如果这对您来说是一个常数,则将其称为常数(并大写)。如果没有,那就不要。基本上,这取决于您将对该对象进行的处理以及对它的期望。将每个私有静态最终引用视为唯一签名的常量是错误的。因此,我也不会将每个最终变量都称为常量。现在,如果最后一个变量在您的脑海中是一个常数,那就去做。 (4认同)