Explain Codes LogoExplain Codes Logo

Collections.emptylist() returns a List<Object>?

java
type-inference
collections
best-practices
Alex KataevbyAlex Kataev·Oct 4, 2024
TLDR

Collections.emptyList() produces a type-safe List<?>, whose generic type is inferred based on its usage context. For a specific type, you can utilize Collections.<Type>emptyList() to explicitly denote the list's element type.

List<String> emptyStrings = Collections.emptyList(); // Picked up as List<String> in Hogwarts' Sorting Hat style

Being immutable, attempting to add an element will result in an UnsupportedOperationException.

When not given a type hint, using Collections.emptyList() without a type parameter defaults to List<Object>. To thwart this, either assign it to a specific type variable or use type casting.

Getting the typing right

Don't forget the type parameter

To get a typed empty list, provide a type argument:

List<String> strings = Collections.<String>emptyList(); // It's String like an opera Singer

This shows that Java infers emptyList() as List<String> because of the context.

Assigning emptiness

Attach it to a type variable to enforce the type:

List<Integer> numbers = Collections.emptyList(); // Now it's full of numberless numbers

Again, the context influences the type inference.

Calling it right

When type can't be inferred from assignment, e.g., passing emptyList() directly to a method, be sure to invoke it with a type parameter:

doSomething(Collections.<CustomObject>emptyList()); // It knows how to dress for the occasion

Casting the emptiness

When the generic type isn't clear, you can resort to casting:

List<CustomObject> list = (List<CustomObject>) Collections.emptyList(); // It's a spell like in Harry Potter

But be careful with casting, it has its quirks and may lead to a ClassCastException if not handled confessing "I'm not the object you're looking for!"

All about type inference

The type-checking conundrum

emptyList(), when preferred without an explicit type parameter or without being assigned to a specific type variable, dresses as a List of Object or a blob of clay ready to morph:

List shapelessList = Collections.emptyList(); // It's raw, like sushi!

An insight into the Matrix

Inspecting the source code explains how Collections.emptyList() has the chameleon-like nature to return a list of any type:

public static final <T> List<T> emptyList() { @SuppressWarnings("unchecked") List<T> t = (List<T>) EMPTY_LIST; return t; }

The EMPTY_LIST is a raw list that gets morphed to List<T> during runtime.

Make use of emptyList()

In immutable collections

Being an immutable singleton instance, emptyList() is perfect when you want a list that won't change:

List<?> listOne = Collections.emptyList(); List<?> listTwo = Collections.emptyList(); assert listOne == listTwo; // Just like each half of a Kit-Kat Twin Bar!

With optional parameters

To avoid method duplication with optional list parameters, choose emptyList():

public void processItems(List<String> items) { // Whatever each String hides, we got it sorted here! } // Instead of overloading, simply call it with: processItems(Collections.<String>emptyList()); // It does the heavy lifting with elegant grace!

In safe-returning methods

Return emptyList() rather than null and leave the burden of null checks behind

public List<String> getItems() { if (/* condition */) { return Collections.emptyList(); // No strings attached! } // Other logic }

EMPTY_LIST vs. emptyList(): The duel

Of Static and Generic singletons

While EMPTY_LIST is an ageless static instance with raw power, emptyList() is a more refined generic concoction:

List rawList = Collections.EMPTY_LIST; // It's as basic as pizza without toppings List<String> emptyList = Collections.emptyList(); // It's got a secret identity, like Spider-Man!

Use EMPTY_LIST safely

Although tempting, overusing EMPTY_LIST may cause unchecked warnings, emptyList(), however, is more prudent and type-safe:

List<String> list = Collections.EMPTY_LIST; // A reckless hedgehog crossing the intersection