使用 Date 或 List 在简单的 getter 和 setter 方法中解决 Sonar 问题

Solve Sonar issue in simple getter and setter methods with Date or List

我写这个 getter/setter 以从 Eclipse 源菜单中列出:

public Date getDate() {
    return date;
}

public void setDate(Date date) {
    this.date = date;
}

Sonar 报告两个问题:

Return a copy of "date" & Store a copy of "date"

有解释

"Mutable members should not be stored or returned directly"

和示例代码:

public String [] getStrings() {
    return strings.clone();}

public void setStrings(String [] strings) {
    this.strings = strings.clone();}

我认为如果我的日期为空,它会抛出 NullPointerException。然后我将代码更改为:

public Date getDate() {
    if (this.date != null) {
        return new Date(this.date.getTime());
    } else {
        return null;
    }
}

public void setDate(Date date) {
    if (date != null) {
        this.date = new Date(date.getTime());
    } else {
        this.date = null;
    }
}

现在标记其他问题:

"Assigning an Object to null is a code smell. Consider refactoring".

我在互联网上搜索并设置或 return 一个新数组对我来说不是解决方案,如果 setter 参数为空以覆盖一个,我想将我的列表保留为空现有的先前列表。

我对 List 也有同样的问题,我想 return/preserve null 而不是一个空列表的新 ArrayList。在这种情况下,setter 标记了另一个问题:

"Return an empty collection instead of null.".

这个问题的解决方案是什么?

如果您在 Java 8 并且不想处理空日期,那么使用 Optional 可能会对您有所帮助。

编辑:您的 "POJO" class

示例
public class Toto {

    public Optional<Date> myDate;

    public Optional<Date> getMyDate() {
        return this.myDate;
    }

    public void setMyDate(final Date myDate) {
        this.myDate = Optional.ofNullable(myDate);
    }

}

代码使用示例:

Toto toto = new Toto();
toto.setMyDate(null);
System.out.println("Value is null ? " + toto.getMyDate().isPresent());
System.out.println("Value: " + toto.getMyDate().orElse(new Date()));

尝试用具体的日期值更改 toto.setMyDate(...) 看看会发生什么。

如果您不知道什么是 Optional 或如何使用它,您可以找到大量示例。

BUT :这只是解决违规问题的一种方法,我完全同意 Brad 的评论,Optional 并非旨在用作一种类型,而更像是合同为潜在的空/空 returns。 一般来说,如果违规不正确,您不应该仅仅为了修复违规而以错误的方式更正您的代码。在你的情况下,我认为你应该忽略违规行为(不幸的是 Sonar 的大多数违规行为)

如果你真的想在代码中使用 Java 8 和 Optional,那么你的 POJO class 会像这样(仅在 getter 上使用 Optional 作为对比)

public class Toto {


    public Date myDate;

    public Optional<Date> getMyDate() {
        return Optional.ofNullable(this.myDate);
    }

    public void setMyDate(final Date myDate) {
        this.myDate = myDate;
    }

}

这样,

  • 你 bean 保持可序列化(可选不是)
  • 您仍然启用 "client" 代码以选择如何处理 属性
  • 的空值/空值
  • 将您的 Sonar 违规配置为误报,因为这是您想要的而不是更改您的代码

您不必在 setter 中明确设置 null,只需像这样使用传入的值...

public void setDate(Date date) {
    if (date != null) {
        this.date = new Date(date.getTime());
    } else {
        this.date = date;
    }
}

就我个人而言,我绝不会在可能的情况下允许空值进入我的 Value 对象,但这只是我固执己见的编码风格。

我对任何人的建议是更喜欢在构造函数中设置所有值并且不允许空值的不可变值对象。这种样式可能不适合所有期望 java bean getter/setters 所以要知道在哪里可以有效地使用它来简化你的代码。

编辑

如果上面的代码仍然给你警告,你必须有 "property is not set yet" 功能,另一种方法是定义一个 "null object" 像这样

public static final Date NO_DATE = new Date(Long.MIN_VALUE);

public void setDate(Date date) {
    this.date = (date == null) ? NO_DATE : new Date(date.getTime());
}

此 class 的用户可以像这样引用 NO_DATE 对象,这仍然有助于代码的可读性

if(toto.getDate() != NO_DATE) ...

或者把它封装到另一个方法中这样使用

if(toto.hasDate()) ...

当然,这并没有比@kij 的Java 8 可选方法增加多少好处,但它确实适用于任何版本的Java

一般来说,虽然使用静态分析工具来验证代码是有价值的,但你不应该盲目修复每一个弹出的警告。您需要分析触发的问题并检查它是否真的适用于您的上下文。

现在解决你提到的问题

Return a copy of "date" & Store a copy of "date"

这似乎是有效的。最好的做法是采取防御措施,不要通过 getters/setters 公开可变状态。因此,应该在 getter/setter 中创建一个防御副本。这可以按照您的方式完成,或者使用新的 Java Time API,它提供不可变的对象。

Assigning an Object to null is a code smell. Consider refactoring

IMO 可疑的一个。这个问题是由 PMD 插件(它是分析代码的工具,SonarQube 正在显示报告)引起的。此规则 http://pmd.sourceforge.net/pmd-4.3.0/rules/controversial.html#NullAssignment 引发了问题,如您所见,它属于有争议的类别。我认为您的代码没有任何问题,正确的操作可能是忽略此警告并将问题标记为 "won't fix"。您还可以将 SonarQube 配置为不在质量配置文件设置中使用此特定规则。

Return an empty collection instead of null.

您没有提供触发它的代码,但这似乎是一条有效的建议。通常 return 空集合比空值更好。