关于此事的对话似乎与命名interface
和abstract
类的对话相反。我认为这令人担忧,并认为这个决定比简单选择一个命名约定并始终使用它与static final
一起深入。
抽象类和接口
在命名接口和抽象类时,公认的约定已经演变成不使用任何标识信息来作为前缀或后缀,以表明其不同于其他类的是一个abstract class
或interface
。
public interface Reader {}
public abstract class FileReader implements Reader {}
public class XmlFileReader extends FileReader {}
据说开发人员不需要知道以上类是抽象的
还是一个接口
。
静态常量
我个人的偏好和信仰是,当涉及到静态常量
变量时,我们应该遵循类似的逻辑来确定如何命名它。似乎全大写的参数是从C和C++语言中盲目地采用的一种传统。在我的评估中,这不是继续在Java中保留这种传统的正当理由。
意图问题
我们应该问自己静态常量
在自己的上下文中的作用是什么。以下是三个例子,说明了在不同的上下文中如何使用静态常量
:
public class ChatMessage {
private static final Logger logger = LoggerFactory.getLogger(XmlFileReader.class);
public class Error {
public static final int Success = 0;
public static final int TooLong = 1;
public static final int IllegalCharacters = 2;
}
public static final int MAX_SIZE = Integer.MAX_VALUE;
}
在这三种情况下都使用大写字母吗?当然可以,但我认为这可能会削弱每种情况的目的。因此,让我们逐个检查每种情况。
目的:私有变量
以上面的Logger
示例为例,记录器被声明为私有变量,并且只会在类内部或可能的内部类中使用。即使它被声明为protected
或package
可见性,其用法也是相同的:
public void send(final String message) {
logger.info("Sending the following message: '" + message + "'.");
}
我们不关心logger是一个static final
的成员变量,它也可以只是一个final
实例变量。我们并不知道具体情况,也无需了解。我们只需要知道使用类实例提供的记录器来记录消息即可。
public class ChatMessage {
private final Logger logger = LoggerFactory.getLogger(getClass());
}
在这种情况下,你不会把它命名为 LOGGER
,那么如果它是 static final
,为什么要全部大写命名呢?在两种情况下,它的上下文或意图是相同的。
注意:我改变了关于 package
可见性的立场,因为它更像是一种受限于 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.");
}
}
}
以上有多种变化,都能达到允许显式转换
enum -> int
和
int -> enum
的同样目的。在网络流传输这些信息时,本地Java序列化过于冗长。使用简单的
int
、
short
或
byte
可以节省大量带宽。我可以详细比较
enum
与
static final int
之间的优缺点,包括类型安全性、可读性、可维护性等方面;幸运的是,这超出了本讨论的范围。
归根结底,有时候static final int
会被用作enum
风格的结构。
如果你能接受上述陈述为真,我们就可以继续讨论风格问题。在声明
enum
时,通常不会这样做:
public enum Error {
SUCCESS(0),
TOOLONG(1),
ILLEGALCHARACTERS(2);
}
相反,我们执行以下操作:
public enum Error {
Success(0),
TooLong(1),
IllegalCharacters(2);
}
如果你的 static final
整数块用作松散的 enum
,那么为什么要为其使用不同的命名约定?无论在哪种情况下,它的上下文或意图都是相同的。
目的:静态、常量、公共属性
这个用例可能是最模糊和有争议的。静态常量 size 的用法示例是其中最常见的。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]
这个文件包含一系列通过序列化成字节流并写入该文件的MyObject
对象。该文件包含325个MyObject
对象的字节,但是不知道每个MyObject
的大小,你就无法知道哪些字节属于每个MyObject
。因此,你需要在MyObject
中定义MyObject
的大小:
public class MyObject {
private final long id;
private final int value;
private final boolean special;
public static final int SIZE = 13;
}
在上述定义中,MyObject
数据结构被写入文件时占据13个字节。知道这一点后,当我们读取二进制文件时,可以动态地确定在文件中跟随多少个MyObject
对象:
int dataSize = buffer.getInt();
int totalObjects = dataSize / MyObject.SIZE;
这似乎是所有全大写的
static final
常量的典型用例和参数,我同意在这种情况下,全部大写是有意义的。原因如下:
Java没有像C语言那样的
struct
类,但是
struct
只是一个所有成员都是公共的、没有构造函数的类。它只是一种数据结构。因此,您可以以
struct
的方式声明一个
class
:
public class MyFile {
public static final int MYFM = 0x4D59464D;
public static class MyFileHeader {
public int fileType = MYFM;
public int version = 0;
public int dataSize = 0;
public int checksumSize = 0;
}
}
首先声明,我个人不会以这种方式解析。我建议使用一个不可变的类,该类通过接受ByteBuffer
或所有4个变量作为构造函数参数在内部处理解析。话虽如此,在这种情况下访问(在这种情况下设置)此struct
的成员可能看起来像:
MyFileHeader header = new MyFileHeader();
header.fileType = buffer.getInt();
header.version = buffer.getInt();
header.dataSize = buffer.getInt();
header.checksumSize = buffer.getInt();
这些不是static
或final
,但它们是公开暴露的成员,可以直接设置。因此,我认为当一个static final
成员公开显示时,将其全部大写是有意义的。这是唯一需要与公共非静态变量区分的重要时刻。
注意:即使在这种情况下,如果开发人员尝试设置final
变量,他们也会遇到IDE或编译器错误。
摘要
总之,您选择的static final
变量约定将成为您的首选项,但我坚信使用环境应在设计决策中占主导地位。我的个人建议是遵循以下两种方法之一:
方法1:评估上下文和意图[高度主观;逻辑]
- 如果是应该与私有实例变量无法区分的
private
变量,则将它们命名为相同的名称。全部小写
- 如果它的意图是作为一组
static
值的松散枚举风格块,则将其命名为enum
。 帕斯卡命名法:每个单词首字母大写
- 如果其意图是定义一些公开可访问的、常量和静态属性,则通过使其全部大写来突出显示它
方法2:私有 vs 公共[客观;逻辑]
方法2将其上下文压缩为可见性,并且不留任何解释余地。
- 如果它是
private
或protected
,则应全部小写。
- 如果它是
public
或package
,则应全部大写。
结论
这就是我看待static final
变量命名约定的方式。我认为它不是可以或应该被装箱成一个单独的全部捕获的东西。我相信您应该在决定如何命名之前评估其意图。
但是,主要目标应尽力在整个项目/包的范围内保持一致。最终,这是您所掌控的所有内容。
(我预计会遭到反抗,但也希望从社区中获得对这种方法的支持。无论您的立场如何,请在反驳、批评或赞扬这种风格选择时保持礼貌。)
LOG.info(“LOG not log!”):)
- Thomas