静态最终变量的 Java 命名约定静态决赛总结

2022-08-31 09:38:35

有一条规则说:

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

这适用于整数或字符串等基元类型:

private static final int MAX_COUNT = 10;

但是什么是非基元类型呢?在大多数情况下,我看到了以下内容:

private static final Logger log = Logger.getLogger(MyClass.class);

或在单例中,其中实例变量不是大写的。

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


答案 1

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


接口类型中的常量名称应该是大写的,类类型的变量通常可以是一个或多个单词、首字母缩略词或缩写的序列,全部大写,组件由下划线字符分隔。常量名称应是描述性的,而不是不必要的缩写。传统上,它们可以是任何适当的词性。常量名称的示例包括 类 的 、 、 和 。final"_"MIN_VALUEMAX_VALUEMIN_RADIXMAX_RADIXCharacter

表示集合的替代值的一组常量,或者不太常见的是,在整数值中屏蔽位,有时使用通用首字母缩略词作为名称前缀来指定,例如:

interface ProcessStates {
  int PS_RUNNING = 0;
  int PS_SUSPENDED = 1;
}

涉及常量名称的模糊是罕见的:

  • 常量名称通常没有小写字母,因此它们通常不会遮盖包或类型的名称,它们通常也不会隐藏字段,其名称通常至少包含一个小写字母。
  • 常量名称不能模糊方法名称,因为它们在语法上是可区分的。

答案 2

关于这个问题的对话似乎是关于命名和类的对话的对立面。我发现这令人震惊,并认为这个决定比简单地选择一个命名约定并始终使用它要深入得多。interfaceabstractstatic final

抽象和接口

在命名接口和抽象类时,公认的约定已经演变为不以 your 或 name 作为前缀或后缀,也不添加任何标识信息,以指示它不是类。abstract classinterface

public interface Reader {}
public abstract class FileReader implements Reader {}
public class XmlFileReader extends FileReader {}

据说开发人员不需要知道上述类是或.abstractinterface

静态决赛

我个人的偏好和信念是,在提到变量时,我们应该遵循类似的逻辑。相反,我们在确定如何命名时会评估其用法。似乎全大写的论点是从C语言和C++语言中盲目采用的东西。在我看来,这并不是在Java中延续传统的理由。static final

意图问题

我们应该问问自己,在我们自己的语境中,它的功能是什么。以下是如何在不同上下文中使用的三个示例static finalstatic 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;
}

是否可以在所有三种情况下都使用全部大写?当然可以,但我认为可以说这会减损每个目的。因此,让我们单独检查每个案例。


用途:私有变量

在上面的示例中,记录器被声明为私有,并且只能在类中使用,或者可能是内部类。即使它是在 可见性 处声明的,它的用法也是相同的:Loggerprotected

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

在这里,我们不关心这是一个成员变量。它可能只是一个实例变量。不知道。我们不需要知道。我们需要知道的是,我们正在将消息记录到类实例提供的记录器中。loggerstatic finalfinal

public class ChatMessage {
    private final Logger logger = LoggerFactory.getLogger(getClass());
}

在这种情况下,您不会将其命名为,那么如果它是,为什么要将其全部命名为大写?在这两种情况下,其背景或意图是相同的。LOGGERstatic final

注意:我改变了我对软件包可见性的立场,因为它更像是一种公共访问形式,仅限于软件包级别。


用途:枚举

现在你可能会说,你为什么要用整数作为?这是一个仍在发展的讨论,我甚至会说是半有争议的,所以我会尽量不通过冒险来破坏这个讨论。但是,建议您实现以下可接受的枚举模式:static finalenum

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.");
        }
    }
}

上述方法有一些变体,它们实现了允许显式转换 和 的相同目的。在通过网络流式传输此信息的范围内,本机 Java 序列化实在是太冗长了。一个简单的, ,或者可以节省巨大的带宽。我可以深入研究一个冗长的比较和对比,关于与涉及类型安全性,可读性,可维护性等的利弊;幸运的是,这超出了本次讨论的范围。enum->intint->enumintshortbyteenumstatic final int

底线是这样的,有时静态的最终int将用作枚举样式结构

如果你能让自己接受上述说法是正确的,我们就可以继续讨论风格。在声明 时,接受的样式表示我们不执行以下操作:enum

public enum Error {
    SUCCESS(0),
    TOOLONG(1),
    ILLEGALCHARACTERS(2);
}

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

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

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


用途:静态、常量、公共属性

这个用例可能是所有用例中最模糊和最有争议的。静态常量大小用法示例是最常遇到这种情况的地方。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]

此文件包含序列化为字节流并写入此文件的对象列表。此文件有325个字节的对象,但是如果不知道每个对象的大小,就无法知道哪些字节属于每个。因此,您定义了 上的大小:MyObjectMyObjectMyObjectMyObjectMyObjectMyObject

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
}

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

int dataSize = buffer.getInt();
int totalObjects = dataSize / MyObject.SIZE;

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

Java没有像C语言那样的类,但a只是一个包含所有公共成员而没有构造函数的类。它只是一个数据。因此,您可以以类似的方式声明一个:structstructstructclassstruct

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;
    }
}

让我在此示例的开头声明我个人不会以这种方式解析。我建议使用一个不可变的类,通过接受一个或所有4个变量作为构造函数参数来在内部处理分析。也就是说,访问(在本例中设置)此成员将如下所示:ByteBufferstruct

MyFileHeader header = new MyFileHeader();
header.fileType     = buffer.getInt();
header.version      = buffer.getInt();
header.dataSize     = buffer.getInt();
header.checksumSize = buffer.getInt();

这些不是 或 ,但它们是可以直接设置的公开公开成员。出于这个原因,我认为当一个成员公开时,将其完全大写是有意义的。这是将其与公共非静态变量区分开来的一次重要时刻。staticfinalstatic final

注意:即使在这种情况下,如果开发人员尝试设置最终变量,他们也会遇到 IDE 或编译器错误。


总结

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

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

  • 如果它是一个应该与实例变量无法区分的变量,则将它们命名为相同。全部小写privateprivate
  • 如果它的目的是用作值的松散样式块的类型,请将其命名为 .帕斯卡大小写:每个单词的首字母大写enumstaticenum
  • 如果它的目的是定义一些可公开访问的,常量和静态的属性,那么通过将其全部大写来使其突出

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

方法论2基本上将其上下文浓缩为可见性,并且没有留下任何解释的余地。

  • 如果是或那么它应该全部小写privateprotected
  • 如果是 或 那么 它应该全大写publicpackage

结论

这就是我如何看待变量的命名约定。我不认为这可以或应该被框定为一个单一的包罗万象的东西。我认为,在决定如何命名之前,您应该评估其意图。static final

但是,主要目标应该是尝试在整个项目/包的范围内保持一致。最后,这就是你所能控制的一切。

(我确实希望遇到阻力,但也希望从社区获得一些关于这种方法的支持。无论你的立场如何,在斥责、批评或赞扬这种风格选择时,请保持文明。