What is the Collections.checkedList() call for in java?
Asked Answered
P

4

16

I just want to know for what java.util.Collections.checkedList() is actually used.

I have some code that I know is returning me a List<String> but it's being passed through a chain of messaging calls and returned to me as a java.io.Serializable. Is that checkedList call good for me to turn my Serializable into a List<String>? I know I can cast it to a java.util.List, but I'd rather not have to check each element and I'm not comfortable with assuming each element is a String.

Pili answered 21/7, 2009 at 19:17 Comment(0)
B
19

It is used in part as a debugging tool to find where code inserts a class of the wrong type, in case you see that happening, but can't figure out where.

You could use it as part of a public API that provides a collection and you want to ensure the collection doesn't get anything in it of the wrong type (if for example the client erases the generics).

The way you could use it in your case is:

 Collections.checkedList(
      new ArrayList<String>(uncertainList.size()), String.class)
      .addAll(uncertainList);

If that doesn't throw an exception, then you know you are good. That isn't exactly a performance optimized piece of code, but if the list contents are reasonably small, it should be fine.

Beisel answered 21/7, 2009 at 19:28 Comment(0)
U
19

Not quite:

Collections.checkedList will only decorate the list to prevent any future inserts with objects of the wrong class, it won't check all the elements that are already in the list.

However, you could make a new checkedList, and then call addAll and pass in the list you are unsure about - rather than writing the loop yourself.

Unknown answered 21/7, 2009 at 19:21 Comment(0)
B
19

It is used in part as a debugging tool to find where code inserts a class of the wrong type, in case you see that happening, but can't figure out where.

You could use it as part of a public API that provides a collection and you want to ensure the collection doesn't get anything in it of the wrong type (if for example the client erases the generics).

The way you could use it in your case is:

 Collections.checkedList(
      new ArrayList<String>(uncertainList.size()), String.class)
      .addAll(uncertainList);

If that doesn't throw an exception, then you know you are good. That isn't exactly a performance optimized piece of code, but if the list contents are reasonably small, it should be fine.

Beisel answered 21/7, 2009 at 19:28 Comment(0)
D
11

A discussion of what checkedList could be used for is available in the documentation for checkedCollection. The reasons given are:

  • as a debugging aid (if someone has used an unchecked cast)
  • to ensure safety when passing a collection to be populated by third-party code.

You could use the following from google collections to check that the list does only contain strings:

Iterables.all(list, Predicates.instanceOf(String.class))
Duren answered 21/7, 2009 at 19:31 Comment(1)
Thanks. I missed that. IDEs are spoiling my javadoc reading.Pili
S
0

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.

Straitjacket answered 16/6 at 14:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.