Usage of Collections.EMPTY_LIST

Hi all,

Firstly, thanks for this useful tool.

I’d like to clarify a code smell.
SonarLint along with IntelliJ is reporting as a minor code smell with the following code saying that Collections.EMPTY_LIST should not be used.

if(this.dependencies == Collections.EMPTY_LIST){
this.dependencies = new ArrayList<>();
}

Replace “Collections.EMPTY_LIST” by “Collections.emptyList()”
“Collections.EMPTY_LIST”, “EMPTY_MAP”, and “EMPTY_SET” should not be used

if(this.dependencies == Collections.emptyList()){ // It does not work!
this.dependencies = new ArrayList<>();
}

I’m using the EMPTY_LIST to compare a reference instead of attributing an EMPTY_LIST to a variable.

So, I think SonarLint is only looking for Collections.EMPTY_LIST references in the code instead of analysing the context where it is being used?

Noncompliant Code Example
List collection1 = Collections.EMPTY_LIST; // Noncompliant Map<String, String> collection2 = Collections.EMPTY_MAP; // Noncompliant Set collection3 = Collections.EMPTY_SET; // Noncompliant

Compliant Solution
List collection1 = Collections.emptyList(); Map<String, String> collection2 = Collections.emptyMap(); Set collection3 = Collections.emptySet();

I don’t understand this part. Why it does not work?

emptyList returns the instance kept by EMPTY_LIST:

/**
 * The empty list (immutable).  This list is serializable.
 *
 * @see #emptyList()
 */
@SuppressWarnings("unchecked")
public static final List EMPTY_LIST = new EmptyList<>();

/**
 * Returns the empty list (immutable).  This list is serializable.
 *
 * <p>This example illustrates the type-safe way to obtain an empty list:
 * <pre>
 *     List&lt;String&gt; s = Collections.emptyList();
 * </pre>
 * Implementation note:  Implementations of this method need not
 * create a separate <tt>List</tt> object for each call.   Using this
 * method is likely to have comparable cost to using the like-named
 * field.  (Unlike this method, the field does not provide type safety.)
 *
 * @see #EMPTY_LIST
 * @since 1.5
 */
@SuppressWarnings("unchecked")
public static final <T> List<T> emptyList() {
    return (List<T>) EMPTY_LIST;
}

I prepared a small example code:

import java.util.Collections;
import java.util.List;

public class Test {

	public static void main(String[] args) {
		
		List dependencies = null;
		System.out.println(dependencies == Collections.EMPTY_LIST);
		System.out.println(dependencies == Collections.emptyList());
		
		dependencies = Collections.emptyList();
		System.out.println(dependencies == Collections.EMPTY_LIST);
		System.out.println(dependencies == Collections.emptyList());
		
		dependencies = Collections.EMPTY_LIST;
		System.out.println(dependencies == Collections.EMPTY_LIST);
		System.out.println(dependencies == Collections.emptyList());
		
		System.out.println(Collections.EMPTY_LIST == Collections.emptyList());
	}
}

and the result is as I expected:

false
false
true
true
true
true
true

Hi Adam,

Sorry, I think could have added more details about my dependencies type.

Your example works because you did not specify the List type but thinking a little bit more now, I could have specified the type when using Collections.emptyList(), likewise “this.dependencies == Collections.emptyList()”.

private List dependencies = Collections.emptyList();

if(this.dependencies == Collections.emptyList()){ //Now it works.
this.dependencies = new ArrayList<>();
}

Would be helpful to see such example as part of the “Compliant Solution”. What do you think?

Exactly. This code works exactly the same as the previous:

import java.util.Collections;
import java.util.List;

public class Test {

    public static void main(String[] args) {
        
        List<String> dependencies = null;
        System.out.println(dependencies == Collections.EMPTY_LIST);
        System.out.println(dependencies == Collections.<String>emptyList());
        
        dependencies = Collections.emptyList();
        System.out.println(dependencies == Collections.EMPTY_LIST);
        System.out.println(dependencies == Collections.<String>emptyList());
        
        dependencies = Collections.EMPTY_LIST;
        System.out.println(dependencies == Collections.EMPTY_LIST);
        System.out.println(dependencies == Collections.<String>emptyList());
        
        System.out.println(Collections.EMPTY_LIST == Collections.emptyList());
    }
}

I’m not a SonarSourcer, so I cannot help you in this area :wink: