在使用 Collections.emptyList() 声明后,延迟将列表重新初始化为可修改

Lazy reinit a list to modifiable after declaring with Collections.emptyList()

想要将列表声明为 List<String> info = Collections.emptyList() 但是当用户调用 add(String msg) 然后重新初始化到一个可修改的列表时。

这样正确吗:

private List<String> info = Collections.emptyList();
public void addInfo(String s){
        final List<String> e = Collections.emptyList();
        if(info == e){
            info = new ArrayList<>();
        }
        info.add(s);
    }

if(info.equals(e)){

如果我有其中的 3 个,我可以得到这个通用代码:

public void addInfo(String s) {
        info = addTo(s, info);
    }
    public void addWarn(String s) {
        warn = addTo(s, warn);
    }

    public void addErr(String s) {
        errs = addTo(s, errs);
    }

    private List<String> addTo(String s, @org.jetbrains.annotations.NotNull List<String> t){
        final List<String> e = Collections.emptyList();
        if(t.equals(e)){
            t = new ArrayList<>();
        }
        t.add(s);
        return t;
    }

我想由于正在创建新列表,以下内容将无法正常工作?

private void addTo(String s, @org.jetbrains.annotations.NotNull List<String> t){
    final List<String> e = Collections.emptyList();
    if(t.equals(e)){
        t = new ArrayList<>();
    }
    t.add(s); 
}

考虑您的代码:

    final List<String> e = Collections.emptyList();
    if(info == e){
        info = new ArrayList<>();
    }
    info.add(s);

我不相信 Java API 中有任何保证相同的引用将始终从 emptyList() 返回(javadoc 声明“此方法的实现不需要为每个调用创建一个单独的列表对象")。

鉴于您可能会修改列表,因此使用 new ArrayList<>() 初始化比 emptyList() 更有意义。使用您可能想要修改的不可修改列表确实没有多大意义。

但是,如果您出于某种原因确实需要使用 emptyList(),那么也许:

if (info.isEmpty())
    info = new ArrayList<>();

鉴于您即将向其中添加一个项目,此测试无论如何只会通过一次。

使用 .equals 是唯一正确的解决方案 -- 但等同于更简单的 info.isEmpty().

请注意,即使 Collections.emptyList() 总是 returns 在 Collections.EMPTY_LIST 中保存的一个实例,参考比较也不会检测调用者何时使用 JDK 9+ List.of() 初始化字段。另一方面,非空也不能保证可变性。

整个逻辑仅适用于 private 方法,所有调用者及其用法都是已知的。

但是您应该考虑完全放弃这些特殊情况的替代方案。自 Java 8 起,默认构造函数 new ArrayList<>() 将不会创建后备数组。它被推迟到第一次添加元素时。

所以你可以用普通的 new ArrayList<>() 初始化所有字段,并用普通的 add 调用实现 addInfoaddWarnaddErr,摆脱 addTo 方法、条件和重复赋值。甚至声明字段 final 也是可能的。虽然仍然不需要大量内存来存储未使用的列表。