Let's imagine the following scenario: you have a list of which type you know, for example, a list of numbers. And you have a 3rd party API that takes a list (without specifying the type). This API may cause some mutations in the input list (let's say), and you want to make sure the 3rd party API is not adding another type to your list.
A little bit of context: (please read more about Erasures) at the runtime, the generics become Object.
Let's say you have the following method:
private static final String STRING_VALUE = "this is actually a string!";
public void addElements(List l) {
l.add(STRING_VALUE);
System.out.println(l);
}
It's okay for compile time because the List doesn't specify a type, but you've got unsafe code, this is one example of code that Ben was talking about.
And let's test this method on an array list of numbers:
@Test
public void addingAStringInANumbersList_shouldBeSuccessful() {
List<Number> numbers = new ArrayList<>(List.of(1, 2, 3, 1.0, 2.0f, 3.0f, 5));
addElements(numbers);
Assertions.assertEquals(List.of(1, 2, 3, 1.0, 2.0f, 3.0f, 5, STRING_VALUE), numbers);
}
If you run the above code, it will pass. Why? Because the generics are replaced with Object at runtime, the list is type-safe at runtime, just at runtime.
Let's try with a checked collection:
@Test
public void addAStringInACheckedList_shouldFail() {
List<Number> numbers = Collections.checkedList(new ArrayList<>(List.of(1, 2, 3, 1.0, 2.0f, 3.0f, 5)), Number.class);
Assertions.assertThrows(ClassCastException.class, () -> addElements(numbers));
}
This will throw a ClassCastException, which is expected by the test => It will pass. So, the checked collections help you with legacy 3rd party code.
PS: The Collections utility class only creates wrappers, so if you're passing the "core" collection to the unsafe 3rd party code, the result will be visible on the checked collection wrapper, e.g.:
@Test
public void addingAStringInANumbersList_theCheckedListIsJustAWrapper_shouldBeSuccessful() {
List<Number> numbers = new ArrayList<>(List.of(1, 2, 3, 1.0, 2.0f, 3.0f, 5));
List<Number> checkedNumbers = Collections.checkedList(numbers, Number.class);
addElements(numbers);
Assertions.assertEquals(List.of(1, 2, 3, 1.0, 2.0f, 3.0f, 5, STRING_VALUE), numbers);
Assertions.assertEquals(List.of(1, 2, 3, 1.0, 2.0f, 3.0f, 5, STRING_VALUE), checkedNumbers);
}
I hope the purpose of checked collections is clear now.