Search code examples
javareferencenew-operator

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


Want to declare a list as List<String> info = Collections.emptyList() but when user calls add(String msg) then re-init to a modifiable list.

Is this the correct way:

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);
    }

Or

if(info.equals(e)){

If I have 3 of these can I have this common code :

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;
    }

I guess the following wont work due to the new list being created?

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); 
}

Solution

  • Note that even if Collections.emptyList() always returns the one instance held in Collections.EMPTY_LIST, a reference comparison does not detect when a caller used JDK 9+ List.of() to initialize the field. On the other hand, being non-empty does not guaranty mutability either.

    The entire logic is suitable only for a private method were all callers and their usage are known.

    But you should consider the alternative of dropping these special cases altogether. Since Java 8, the default constructor new ArrayList<>() will not create a backing array. It is deferred until the first addition of an element.

    So you can initialize all fields with a plain new ArrayList<>() and implement the addInfo, addWarn, and addErr with a plain add call, getting rid of the addTo method, the conditionals, and the repeated assignments. Even declaring the fields final is possible. While still not requiring a significant amount of memory for the unused lists.