【问题标题】:Java naming convention for static final variables [duplicate]静态最终变量的Java命名约定[重复]
【发布时间】:2011-11-07 18:00:12
【问题描述】:

有一条规则说:

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

适用于 int 或字符串等原始类型:

private static final int MAX_COUNT = 10;

但是关于非原始类型呢?在大多数情况下,我看到了以下情况:

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

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

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

【问题讨论】:

  • 无论变量的类型如何,都适用约定。违反此规则是该规则的例外,以方便打字或简单的拼写错误。
  • 我通常将其命名为LOG - 但我的同事似乎不喜欢这样称呼。喜欢LOG.info("LOG not log!") :)
  • 我通常将其命名为LOGGER。这是一个常数。
  • 这不是一个规则,它是一个规范的约定,就像所有的一致性一样,它是弱者的避难所。 java中有许多约定需要被忽略——这就是其中之一。它基本上是向匈牙利表示法倾斜,这总是一个非常糟糕的主意。例如,当您决定这不再是一个常量(静态最终)时会发生什么?好吧,如果我们幸运的话,我们有一个重构工具,比如 IntelliJ 或 Eclipse,可以对符号的每次使用进行更改,但是如果我们不走运,我们有 vim,这样的更改会变得非常痛苦。t
  • 一致性是以后必须阅读我的代码的人的避难所,不需要了解我认为我比社区共识更聪明的所有原因。

标签: java coding-style


【解决方案1】:

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


接口类型中的常量名应该是,final 类类型的变量通常可以是一个或一个序列 更多单词、首字母缩写词或缩写词,全部大写,带有组件 下划线"_" 字符分隔。常量名应该是 描述性的,而不是不必要的缩写。按照惯例,他们可能 成为任何适当的词性。常量名称示例 包括MIN_VALUEMAX_VALUEMIN_RADIXMAX_RADIX 的 班级Character

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

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

涉及常量名的混淆很少见:

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

【讨论】:

  • "...类类型的最终变量可能通常是..."
  • 它们本身就是字段,那么为什么要考虑obscuring a field?我可以看出隐藏类型名称的意义,但你不能完全有一个不会由 IDE/编译器处理的冲突。假设你有class Errorstatic final int Error = 0; 碰撞会在哪里出现?您不能将 Error 实例分配给 int 变量。您不能将 int 分配给 Error 变量。我不明白在这种情况下怎么可能发生碰撞。您能否提供一个示例,说明共享相同名称的类型会导致真正的冲突?
  • @crush:在您的示例中,冲突将发生在代码读者的脑海中。换句话说,这是没有充分理由的混乱。我可以看到一些静态常量的 lowerCamelcase 字段名称。我自己做过。我知道约定是大写的。
  • UPPER_CASE 约定是否适用于final non-static local 变量,例如void method() { final int VALUE = 5; /* ... */ }
  • @mre,是的,这就是我最终所做的。谢谢你的建议。我只是认为为此发布一个完整的问题会有点矫枉过正。
【解决方案2】:

在我看来,“常量”变量通常是一个实现细节,并不一定证明不同的命名约定是合理的。它可能有助于提高可读性,但在某些情况下也可能会损害它。

【讨论】:

  • 这是一个约定,使用与否取决于您。但你越坚持越好。
【解决方案3】:

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

private static final 不是将某物定义为常量或非常量的定义。这只是 Java 定义常量的方式,但这并不意味着每个private static final 声明都被放在那里来定义一个常量。

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

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

【讨论】:

  • 没有staticfinal 也是常量吗?如果不是,它叫什么?
  • 我想你是说如果我愿意,我可以把它称为常数——但我真正想说的是,如果我应该大写 final 变量
  • 常量的旧概念已经丢失。用于指向实际固定值的常量。现在你有一个可以改变其状态的对象的常量引用。如果这对您来说是一个常数,请将其称为常数(并将其大写)。如果没有,那就不要。它基本上归结为您对该对象的处理方式以及您对它的期望。考虑到每个私有静态最终引用都是其唯一签名的常量是错误的。所以我也不会将每个最终变量都称为常量。现在,如果最终变量在您的脑海中是一个常数,那就去做吧。
【解决方案4】:

关于此的对话似乎与命名interfaceabstract 类的对话相反。我发现这令人震惊,并认为这个决定比简单地选择一个命名约定并始终与static final 一起使用要深入得多。

抽象与接口

在命名接口和抽象类时,已接受的约定已演变为不在您的 abstract classinterface 前缀或后缀加上任何表明它不是类的任何标识信息。

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

据说开发者不需要知道上面的类是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;
}

你能在所有三种情况下都使用大写吗?绝对可以,但我认为可以说这会减损每个人的目的。因此,让我们分别检查每个案例。


目的:私有变量

在上面的Logger 示例中,记录器被声明为私有,并且只会在类中使用,或者可能在内部类中使用。即使在protectedpackage可见性处声明,其用法也是一样的:

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

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

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

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

注意:我颠倒了我对package 可见性的立场,因为它更像是public 访问的一种形式,仅限于package 级别。


目的:枚举

现在你可能会说,你为什么使用static final 整数作为enum?那是discussion that is still evolving,我什至会说是有争议的,所以我会尽量不要通过冒险来长时间地破坏这个讨论。但是,建议您可以实现以下公认的枚举模式:

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->intint->enum 的相同目的。在通过网络流式传输这些信息的范围内,本机 Java 序列化实在是太冗长了。一个简单的intshortbyte 可以节省大量带宽。我可以深入比较和对比enumstatic 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 removes the need for 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; //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
}

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

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

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

Java 没有像 C 语言那样的 struct 类,但 struct 只是一个具有所有公共成员且没有构造函数的类。这只是一个数据structure。所以,你可以像时尚一样在struct 中声明一个class

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

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

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

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

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


总结

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

方法 1:评估上下文和意图[highly subjective; logical]

  • 如果它是一个private 变量,应该与private 实例变量无法区分,那么将它们命名为相同。 全部小写
  • 如果打算用作static 值的松散enum 样式块,则将其命名为enumpascal case:每个单词的首字母大写
  • 如果打算定义一些可公开访问的常量和静态属性,则将其设为全部大写以使其脱颖而出

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

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

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

结论

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

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

(我确实希望会遇到阻力,但也希望在这种方法上获得社区的一些支持。无论你的立场是什么,在谴责、批评或赞扬这种风格选择时,请保持文明。)

【讨论】:

  • 拒绝投票的原因并非处处大写。
  • @baba 之所以被否决,是因为不遵循现代开发环境已经过时的盲目约定?逻辑很好。
  • @crush,常量总是大写,不管它们的类型和可见性,不管它们是否是原始的。这就是它的样子,它应该是语言规范所指定的样子。 IDE 与它无关。记录器不是常量,因为它们具有功能,因此它们不应该被资本化。看,简单,最重要的是,符合标准。
  • @NikolaYovchev 为什么需要通过命名约定知道变量是常量?仅仅因为标准这么说并不是不发展的好理由。显然,我知道标准所说的内容,并选择提供偏差,因为我们已经超越了需要名称来检查变量特征的时代。
  • 尝试在运行时修改一个静态的最终记录器,看看如何得到你。你忽略了我的问题。
【解决方案5】:

这是一个非常有趣的问题。我会根据它们的类型划分你问题中的两个常量。 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 类型对象使用大写命名约定的原因。

【讨论】:

    【解决方案6】:

    不要狂热地接受 SUN 制定的惯例,做你和你的团队认为正确的事情。

    例如,这就是 eclipse 的做法,打破了惯例。尝试添加implements Serializable,eclipse 会要求为你生成这一行。

    更新:有一些特殊情况被排除在不知道的情况下。然而,我拒绝做你和你的团队看起来合适的事情。

    【讨论】:

    • 这个名字实际上是Java序列化规范的一部分;这是官方认可的违反公约的行为!
    • 有趣的不知道。
    【解决方案7】:

    这些变量是常量,即private static final,无论它们是否全部大写。全大写的约定只是让这些变量更明显是常量,但这不是必需的。我见过

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

    之前是小写的,我很好,因为我知道只使用记录器来记录消息,但它确实违反了约定。您可能会争辩说,将其命名为 log 是一个子约定,我想。但总的来说,以大写命名常量并不是一种正确的方式,而是最好的方式。

    【讨论】:

    • “最佳方式”仍然是主观的 - 这是我对您在这个主题上的回答的一个问题。例如,我根本不相信这是最好的方法。我个人的偏好是static final 变量应该与其他变量没有区别。就像您不会在接口前面加上 IReader 或在后面加上 ReaderInterface 一样。相反,您将其命名为Reader,因为实现细节对开发人员来说并不重要。在记录器的情况下,开发人员真的应该知道或关心logger 是静态最终还是不是静态最终,仅基于名称?我认为不是
    • 确实,在所有大写字母中命名一个常量是匈牙利符号的一种实例(例如,iMyVar 用于 int,sMyVar 用于字符串等)。所以我可以看到不使用大写字母来节省打字。但是,我会说编码器知道特定变量是static final 是有价值的,因为如果他们尝试修改它会得到一个错误。我相信从长远来看这可以节省时间。
    • 编译器和 IDE 应该/将通知他们他们正在尝试设置最终变量的值。不过,我确实相信它在公开可访问变量的情况下是有帮助的。有关更多信息,请参阅下面的论点。
    • 常量值与对对象的常量引用不同。甚至常量引用所引用的对象也不是常量。它仍然可以根据暴露的方法进行更改。所以,你所谓的常数没有什么是不变的。另一方面,一个真正的常数将是 MILLISECONDS_IN_A_SECOND = 1000,例如。
    【解决方案8】:

    语言不在乎。重要的是遵循您正在从事的项目的既定风格和惯例,这样其他维护者(或从现在起五个月后的您)有最大可能不会感到困惑。

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

    【讨论】:

      【解决方案9】:

      没有“正确”的方式——只有约定。您已经陈述了最常见的约定,也是我在自己的代码中遵循的约定:all 静态决赛应该全部大写。我想其他团队遵循其他约定。

      【讨论】:

      • 出于历史原因,我将简单的 final 静态类型标记为全部大写的字符串和数字。我来自 C/C++,按照惯例,常量都用大写字母标记。
      猜你喜欢
      • 1970-01-01
      • 2011-11-25
      • 1970-01-01
      • 2020-05-05
      • 2012-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-20
      相关资源
      最近更新 更多