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 值的副本。
我已经在评论中提到了。我重申一下这些观点,并在下面以全面的方式补充一些新观点。
- 拥有一个 public 日期类型引用并将其视为常量(通过使用 final static 关键字声明它)是一个糟糕的设计,因为它不是常量(日期是可变的)。
- 使用
final
关键字可防止重新赋值,即您不能再次使用 =
赋值运算符。但是 final
关键字不会阻止 .
,点运算符。使用点运算符可以修改日期对象的状态。
Constants.DEFAULT_DATE.setTime(204587433443L)
将修改您假设的固定日期。
- 解决方案 1:您可以将 DEFAULT_DATE 设为私有并创建一个 getter 方法,无论何时调用它,您都可以创建一个新的日期对象,复制 DEFAULT_DATE 和 return 相同的状态。这样即使进行了任何修改,它也会在复制的对象上,而原始对象将保持原样。对于另一个读取原始对象的新副本将 returned.
- 更好的方案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。
我有一个带有私有构造函数的最终 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 值的副本。
我已经在评论中提到了。我重申一下这些观点,并在下面以全面的方式补充一些新观点。
- 拥有一个 public 日期类型引用并将其视为常量(通过使用 final static 关键字声明它)是一个糟糕的设计,因为它不是常量(日期是可变的)。
- 使用
final
关键字可防止重新赋值,即您不能再次使用=
赋值运算符。但是final
关键字不会阻止.
,点运算符。使用点运算符可以修改日期对象的状态。Constants.DEFAULT_DATE.setTime(204587433443L)
将修改您假设的固定日期。 - 解决方案 1:您可以将 DEFAULT_DATE 设为私有并创建一个 getter 方法,无论何时调用它,您都可以创建一个新的日期对象,复制 DEFAULT_DATE 和 return 相同的状态。这样即使进行了任何修改,它也会在复制的对象上,而原始对象将保持原样。对于另一个读取原始对象的新副本将 returned.
- 更好的方案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。