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<String> 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());
}
}
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?