延迟初始化健全性检查:变量可能尚未初始化

Posted

技术标签:

【中文标题】延迟初始化健全性检查:变量可能尚未初始化【英文标题】:Lazy Initialization Sanity Check: Variable might not have been initialized 【发布时间】:2019-09-26 13:04:53 【问题描述】:

我正在组合现有的导入和导出功能,以便在连接被拒绝的情况下减少用户被告知连接被拒绝的次数。我正在调用的库具有单独的导入和导出功能,以及组合的导入/导出功能。导出函数需要要导出的文件列表,而组合函数自己计算出列表(并在内部使用此列表调用导出函数)。由于用户可以关闭导入或导出,因此我只想在必要时获取文件列表。

我想出了这个代码:

List<File> files;
if (mExport)
    files = ListFiles();

if (mExport && mImport && files.size() > 0) // Error is on files
    DoExportAndImport();
else if (mImport)
    DoImport();
else if (mExport && files.size() > 0) // No error here
    DoExport(files);

第二个if 语句被标记为错误Variable 'files' might not have been initialized(但不是最后一个)。

帮助我在这里推理:如果mExport 为真,则files 被初始化;但是如果mExport 是假的,那么第二个if 语句就会短路并且永远不会到达files,所以files 没有被初始化并不重要,因为它没有被使用。

我在这里忽略了什么,还是编译器无法解决这种情况?如果是后者,有没有办法告诉编译器把它关闭,我已经处理好了?

作为记录,在声明中初始化 files = new ArrayList&lt;&gt;() 确实使编译器静音,files = null 也是如此,尽管这显然会导致其他错误;但是初始化一个我知道永远不会被使用或以任何方式引用的值感觉就像是一种浪费。

【问题讨论】:

files = Collections.emptyList() 可能是一个更好的“虚拟”初始化程序,因为它没有分配。 【参考方案1】:

您的逻辑是合理的,因为 files 将始终在每个引用 files 的情况下初始化。但是,正如您所怀疑的那样,编译器并没有走那么远。它的静态分析不考虑变量的值来确定某个条件是否总是真或假。

在这种情况下,如果mExporttrue,编译器不会考虑初始化files。它只看到files 没有从顶部if 语句初始化的可能性,并且它是该语句下方的引用。

您可以通过重新安排逻辑来提供帮助,使其仅在 if 语句块中被引用。

List<File> files;
if (mExport) 
    files = ListFiles();
    if (mImport && files.size() > 0) 
        DoExportAndImport();
     else if (files.size() > 0) 
        DoExport(files);
    
 else if (mImport) 
    DoImport();

【讨论】:

这几乎可以让我到达那里,但如果没有任何文件,它不会调用 DoImport() 。这将需要一个额外的 if 案例来处理。 但它确实给了我灵感,让我在第一个 if 语句中将 mExport 设置为 files.size() > 0 的结果,我认为这会起作用。 不,它只是将投诉转移到我的DoExport(files) 电话。【参考方案2】:

静态分析器不是终极人工智能。有时它可能会给出误报。您应该能够忽略对这一特定行的投诉,但我建议宁愿重新设计整个代码块的逻辑以使其更简单。

另外files = null 是多余的,因为filesnull,除非另外初始化。这只是愚弄分析器。

【讨论】:

以上是关于延迟初始化健全性检查:变量可能尚未初始化的主要内容,如果未能解决你的问题,请参考以下文章

单例模式双重检查锁定与延迟初始化你不得不知道的底层原理

单例模式双重检查锁定与延迟初始化你不得不知道的底层原理

双重检查锁定原理详解

双重检查锁定的单例模式和延迟初始化

双重检查锁定

延迟初始化中的 双重检查模式 和 延迟占位类模式 你都用对了吗?