sonarlint 强制将 final class 的常量声明为受保护

sonarlint is forcing to declare constant of a final class as protected

我有一个带有私有构造函数的最终 class:

public final class Constants {
   public static final Date DEFAULT_DATE;

   static {
    // some code that creates localDate
    DEFAULT_DATE = localDate.toDate();
   }

   private Constants() {
   }
}

sonarlint 发出警告:

 Make DEFAULT_DATE protected
 reason: Mutable fields should not be "public static"

声明它受保护没有意义。 class 被声明为 final - 所以没有继承是可能的。其次,我在其他class中使用DEFAULT_DATE,所以它必须是public。

此外,构造函数被声明为私有的,因此无法创建任何对象。

为什么 sonarlint 强制 DEFAULT_DATE 受保护?

这是因为日期 class 是可变的,所以如果任何对象引用了您的 Constants 对象,它们就可以在您不知情的情况下更改日期值。最好将其设为私有并提供 getter returns 值的副本。

我已经在评论中提到了。我重申一下这些观点,并在下面以全面的方式补充一些新观点。

  1. 拥有一个 public 日期类型引用并将其视为常量(通过使用 final static 关键字声明它)是一个糟糕的设计,因为它不是常量(日期是可变的)。
  2. 使用 final 关键字可防止重新赋值,即您不能再次使用 = 赋值运算符。但是 final 关键字不会阻止 . ,点运算符。使用点运算符可以修改日期对象的状态。 Constants.DEFAULT_DATE.setTime(204587433443L) 将修改您假设的固定日期。
  3. 解决方案 1:您可以将 DEFAULT_DATE 设为私有并创建一个 getter 方法,无论何时调用它,您都可以创建一个新的日期对象,复制 DEFAULT_DATE 和 return 相同的状态。这样即使进行了任何修改,它也会在复制的对象上,而原始对象将保持原样。对于另一个读取原始对象的新副本将 returned.
  4. 更好的方案2:将Date的long值保持为常量。让代码的调用者使用常量 long 值创建日期对象。 (您还可以在代码中的某处添加一个实用方法,如果您可以在创建对应于输入长值。)

您正在执行静态分析工具意味着您拥有代码。即使它是遗留代码,也要重构它。如果它不是你控制的部分,你可以要求处理代码的团队重构它。如果它是某个第三方图书馆的一部分,那么您可能可以向他们发送邮件并等待他们的回复,而且如果图书馆所有者不打算更正此问题,那么您可能应该使用另一个图书馆,因为当前图书馆可能有一些类似的其他问题。

回答我自己的问题:

正如其他人所解释的那样,java.util.Date 是一个可变的,因此将其作为常量提供是一个糟糕的设计,因为它实际上不是。

@nits.kk 给出了更好的解决方案 "Keep the long value of Date as a constant" 并使用让调用者将 long 值转换为 Date 对象。

public static final long DEFAULT_DATE = <long value representing the required date>;

对此以及我现在在我的代码中使用的方式的改进是使用 java.time.Instant,这是一个不可变对象

public static final Instant DEFAULT_DATE = Instant.parse("<ISO-formatted-date>");

这样我保留了预期的 Date 对象,静态检查器将不再有 warning/error。