为什么不使用静态字符串数组?

Why not use static array of Strings?

遇到这个coding standard from Oracle

Do not use a static array of strings.

这个推荐的原因是什么?

数组是可变的容器。没有内置同步的全局可变容器是等待发生的多线程恐怖。您可以在某些本地上下文中存储对该数组的引用,然后在您不知情的情况下随时更改其内容。反之亦然,您可能会假设这是方便的全局状态,但是每个客户端都必须知道同步其对该状态的访问的确切正确方法。有人会忘记,错误就会诞生。

这种方式破坏了面向对象的范式。而且这是一个安全漏洞,因为 final 关键字确保仅具有参考价值,并且内容很容易更改。

其中一些是任意的。例如,单个 return 语句是一种风格问题(可能与过去无法处理多个 return 路径的某些分析工具有关?)。

其中一些已经过时。就像关于 StringBuilding 的那个。

我假设这些是旧的性能建议和风格指南的混合体。我认为 String 静态数组属于后一类,可能暗示了其他人所说的关于 link 引入枚举的可能性(也许这就是为什么特别是字符串)。

当你尝试将它们应用到现代框架时,其中一些实际上是完全不可行的:"Do not use a switch to make a call based on the object type.",例如,是 Akka 的核心 Java (onReceive) 方法,通常在Java...

中的任何类型的模式匹配

虽然我承认这很好奇,但我想说这可能与他们过去使用的某些特定工具或提到的枚举可能性有关。但这只是我的看法,不是确定的答案。

我认为这是由于开发人员使用 String 作为枚举的替代品(很可能是无意的),而 static String[] 是对这些类型进行分组的一种方式。


它可能针对的是撰写本文时常见的设计缺陷

据我们所知,本文可能针对 Java 4- 代码库。

并不是说这是在 Java 4 期间编写的,而是 当时存在的大多数生产代码可能是在 Java 4 期间编写的。没有自动装箱陷阱(这很常见)并且没有提到泛型(原始类型)让我相信这一点。

为什么 Oracle 会执行这样一个神秘的原则?

我不认为它与全局状态有关,因为我确定会提到单例(作为全局状态的顶峰)。

我怀疑并发性是问题所在,因为这篇文章没有提到任何关于跨线程共享数据,甚至使用多线程的事情。您会假设他们会提到有关线程环境的一些事情。

为了类型枚举的目的滥用 String 在过去很常见,所以我相信这个陈述(如果与我认为的相关)在当时更容易理解。我发现这个说法在当时同样神秘,但没有人关心它,导致挖掘时缺少google结果进入这个。

我的回答

时至今日,开发人员仍在误用 String 来获取类型信息。由于缺少枚举,我可以看出开发人员可能会如何按照以下方式做一些事情:

class Card {
    static String HEARTS = "HEARTS";
    static String DIAMONDS = "DIAMONDS";
    static String CLUBS = "CLUBS";
    static String SPADES = "SPADES";

    static String[] CARD_TYPE = {
        HEARTS, DIAMONDS, CLUBS, SPADES
    };

    private String type;
    //...
}

使用数组来轻松循环(就像使用 Enum.values() 一样)。

不赞成使用 String 作为枚举:

  1. 它不是类型安全的。如果 MAGE 可以传递给一个方法,那么任何 String 都可以传递到它所在的位置。这导致我们...

  2. 更容易犯错误。打字错误可能会被忽视,这对于普通开发人员来说可能看起来很小,但对于拥有大型代码库和许多消费者(例如 Oracle)的企业来说可能是灾难性的。例如:隐藏的拼写错误导致比较返回 false。这会阻止服务启动。缺少此服务会导致错误。如果在用户注意到错误之前未发现,可能会导致漏洞利用。

这可以通过使用 Java 的内置类型系统来解决:

abstract class CardType {

}

class Hearts extends CardType {

}

....

枚举卡片类型{ 红心、钻石……; }

这不仅不易出错,而且允许您使用多态性,从而避免检查值以触发特定行为的需要。行为可以包含在类型本身中。


虽然我不能保证这是正确的答案,但它似乎是唯一不依赖于声明中未提及的修饰符的使用的答案。

是的,上面的例子没有使用正确的常量(缺少 final)。但是,尽管常量应该是首选,但它们不需要从这种设计中受益(能够使用 String 来获取类型信息),设计也不需要它工作。由于人们可能并不总是为此使用常量,因此他们可以为此目的省略 final 或“常量数组”。