Sonar 告诉您,您不是在复制selectedFields
数组,而只是存储对该数组的引用。因此,如果调用者稍后要修改数组,它也会修改 BeanResultSetHandler 对象的“内容”,例如使用以下代码:
h = new BeanResultSetHandler(MyClass.class, myFieldsArray);
myFieldsArray[0] = null; // now t.selectFields[0] also is null
myFieldsArray[0] = someObject; // now t.selectFields[0] also references someObject
这是否是一个真正的问题取决于恕我直言,如果调用者可能使用自定义数组,她会在调用构造函数后试图修改该数组。如果在所有实际情况下,参数为 null 或某些 Class.getFields() 的结果,我不会太在意它。
“防御性副本”习惯用法是永远不要存储对可变对象或数组的引用,而是在有疑问时始终克隆,在您的情况下克隆数组:
public BeanResultSetHandler(Class<T> type, Object[] selectedFields) {
this.clas = type;
if (selectedFields == null)
this.selectFields = this.clas.getFields();
else
this.selectFields = selectedFields.clone();
}
问题是克隆会产生性能开销,当这种方法被推广时(例如,当从对象返回某些东西时),将会执行大量无用的克隆。所以我不会开始尝试纠正 Sonar 报告的所有此类问题。
就我个人而言,我倾向于仅在模块边界上应用这些惯用语,例如在用于将服务公开给其他模块的方法中。然后在模块内部,我不使用防御性副本,而是依赖单元测试。