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

时间:2019-06-07 14:28:16

标签: java sonarlint

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

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"

将其声明为受保护是没有意义的。该类被声明为final-因此无法继承。其次,我在其他类中使用DEFAULT_DATE,因此它必须是公共的。

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

为什么sonarlint强迫保护DEFAULT_DATE?

3 个答案:

答案 0 :(得分:1)

这是因为Date类是可变的,因此如果任何对象都引用了您的Constants对象,它们可以在不知情的情况下更改日期值。最好将其设置为私有并提供一个返回值副本的getter。

答案 1 :(得分:1)

我已经在评论中提到了。我重申这些要点,并在下面全面添加一些新要点。

  1. 具有公共Date类型引用并将其视为常量(通过使用最终的静态关键字进行声明)并将其视为常量是一种BAD设计,因为它不是常量(Date是可变的)。
  2. 使用final关键字可以防止重新分配,即,您不能再次使用=分配运算符。但是final关键字不能阻止点运算符.。使用点运算符可以修改日期对象的状态。 Constants.DEFAULT_DATE.setTime(204587433443L)将修改您假定的固定日期。
  3. 解决方案1 ​​:您可以将DEFAULT_DATE设置为私有,并可以创建一个getter方法,并且无论何时调用它,您都可以创建一个新的Date对象,复制DEFAULT_DATE的状态并将其返回。这样,即使进行了任何修改,它也将位于复制的对象上,而原始对象将保持原样。再次读取,将返回原始对象的新副本。
  4. 更好的解决方案2 :将Date的long值保持不变。让代码的调用者使用常量long值创建date对象。 (您还可以在代码中的某个位置添加一个实用程序方法,如果您可以在其中花费较长时间作为输入参数,并在创建与输入long值相对应的日期对象之后返回一个日期对象,则可以是一个实用程序类。)

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

答案 2 :(得分:0)

回答我自己的问题:

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

@ nits.kk提供了一个更好的解决方案,“将Date的长值保留为常量”,并使用让调用者将长值转换为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对象,并且静态检查器将不再发出警告/错误。