Explain Codes LogoExplain Codes Logo

Most efficient way to cast List<Subclass> to List<Baseclass>

java
collections
type-safety
immutable
Alex KataevbyAlex Kataev·Feb 28, 2025
TLDR

To convert List<Subclass> to List<Baseclass> efficiently and reliably, use wildcards:

List<Subclass> subList = new ArrayList<>(); List<? extends Baseclass> baseList = subList;

This is a case of covariance, where subList is seen as a List of its base type. You can iterate over baseList, treating elements as Baseclass. Note: baseList is read-only for Subclass elements because of wildcard use.

Using Collections.unmodifiableList()

When you need a List<Baseclass> to insert Baseclass objects, Collections.unmodifiableList() is handy, offering a read-only view:

List<Baseclass> readOnlyBaseList = Collections.unmodifiableList(subList);

Remember, it's read-only. Any attempts to add elements will lead to rebellion!

List.copyOf() for immutable magic

Introduced in Java 9, List.copyOf() presents an immutable copy of your list, like cloning a prehistoric mammoth:

List<Baseclass> immutableBaseList = List.copyOf(subList);

Optimized to ensure type safety and efficiency, List.copyOf() doesn't create a new list if the original one is immutable and of type Baseclass.

Covariance in arrays — the good and the bad

Let's talk about arrays in Java — they're covariant. This might sound like "overbearing," but I promise it's not!

Subclass[] subArray = new Subclass[10]; Baseclass[] baseArray = subArray; // No complaints here

Juxtapose this with generic collections, where type erasure ensures you pay careful attention to casting to make sure no one's crying at runtime.

Double casting — press the red button

A trick that’s a bit like taking a detour in a race is double casting, which involves casting to a raw type first and then to the desired type:

List rawList = subList; List<Baseclass> baseList = (List<Baseclass>) rawList;

You've bypassed type checking, like a secret agent bypassing security. But handle with care, or you could end up causing heap pollution.

Immutable for safety — putting on the safety harness

Making sure your list is immutable is like putting on a safety harness before rock climbing – it saves you from nasty falls. This applies when using double casting or any form of reference that allows modification, as changing an immutable list is like arguing with a brick wall — you won't get far!

Map and stream for the adventurous

For those thrilling cases when types aren't descending from the same hierarchy, streams with map() lets you convert each element, making you feel a bit like an alchemist:

List<AnotherSubclass> anotherSubList = new ArrayList<>(); List<Baseclass> castedList = anotherSubList.stream() .map(element -> (Baseclass) element) .collect(Collectors.toList());

This approach provides the flexibility to dance with different types while ensuring a safe transformation.

The brilliance of List.copyof(), explained

In the Java SE 19 documentation, List.copyof() is hailed for its utility in casting to ensure immutability, especially when dealing with the stern but fair List.of() to create immutable collections.

Runtime exceptions — the party poopers

Just a heads up, inserting an invalid type in arrays gets you a runtime exception, a bit like a bouncer throwing you out of a club. Generic collections, owing to type erasure, erase their component type at compile time, which could lead to type errors at runtime. Always remember to check your invite!