Since String is a final class, how can a wildcard <? extends String> be correct?

Viewed 54

In a course on Spring Batch, I was surprised to see ? extends String. Since String is a final class, how is this correct?

            public void write(List<? extends String> items) throws Exception {
                System.out.println(String.format("Received list of size: %s", items.size()));
                items.forEach(System.out::println);
            }
3 Answers

You're correct that there will never be another class that extends String. So the only type that can fill that ? is String itself. However, the extends / super still conveys intent. Namely, a List<? extends String> is a read-only list. You can't add elements to it since ? extends String is a producer. A List<String> is potentially a read-write collection. So by writing List<? extends String>, we're conveying to whoever is reading the code (and to the compiler) that we only intend to read values from this List, not write anything back.

Technically you are correct, in this case only a String would work because String is final. Still best to always follow PECS. A more practical example would probably use ? extends CharSequence. For example,

public static void write(List<? extends CharSequence> items) {
    System.out.println(String.format("Received list of size: %s", items.size()));
    items.forEach(System.out::println);
}

Now you can call it with any kind of CharSequence.

public static void main(String[] args) {
    write(List.of(new StringBuilder("Hello"), new StringBuilder("World")));
    write(List.of("Hello", "World"));
}

? extends String is a generics qualifier. What it means in this particular case is that the method expects a List of elements which are compatible with String. There is nothing wrong with this definition - in fact, I would even consider it slightly more accurate and universal than List<String>.

But you're right, since there will never be another String, this is equivalent to List<String> for all practical purposes.

Related