This issue is related to #34535 and #34298.

Converter<String, List<? extends Map<String, ?>>> is incorrectly selected when converting a String to a List<String>.

The following reproducer test fails on all 6.2.x versions except 6.2.3:

package com.example;

import org.junit.jupiter.api.Test;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.convert.support.StringToCollectionConverter;

import java.util.List;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;

class GenericConversionServiceTests {

    private final GenericConversionService conversionService = new GenericConversionService();

    @Test
    @SuppressWarnings("unchecked")
    void stringToListOfString() {
        conversionService.addConverter(new StringToCollectionConverter(conversionService));
        conversionService.addConverter(new StringToListOfMapConverter());

        List<String> result = (List<String>) conversionService.convert("foo,bar",
                TypeDescriptor.valueOf(String.class),
                TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class))
        );

        assertThat(result.get(0)).isEqualTo("foo");
    }

    private static class StringToListOfMapConverter implements Converter<String, List<? extends Map<String, ?>>> {

        @Override
        public List<? extends Map<String, ?>> convert(String source) {
            return List.of(Map.of("bar", source));
        }
    }

    // Using this version of the converter also results in a failure
    private static class StringToListOfMapConverterWithoutWildcard implements Converter<String, List<Map<String, ?>>> {

        @Override
        public List<Map<String, ?>> convert(String source) {
            return List.of(Map.of("bar", source));
        }
    }
}

The exception shows that the StringToListOfMapConverter converter was chosen instead of expected StringToCollectionConverter:

class java.util.ImmutableCollections$Map1 cannot be cast to class java.lang.String (java.util.ImmutableCollections$Map1 and java.lang.String are in module java.base of loader 'bootstrap')
java.lang.ClassCastException: class java.util.ImmutableCollections$Map1 cannot be cast to class java.lang.String (java.util.ImmutableCollections$Map1 and java.lang.String are in module java.base of loader 'bootstrap')
    at org.springframework.core.convert.support.GenericConversionServiceTests.stringToListOfString(GenericConversionServiceTests.java:28)
    at java.base/java.lang.reflect.Method.invoke(Method.java:565)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)

Comment From: sdeleuze

@jhoeller I think the potential issue in play here is that the old behavior fallback is involved too early in GenericConversionService.Converters#find via GenericConversionService.Converters#getRegisteredConverter, and so the execution doesn't have the opportunity to iterate until the expected converter.

I am wondering if we should iterate with the new 6.2.3 behavior only on all sourceCandidates x targetCandidates, then do another full sourceCandidates x targetCandidates iteration with the fallback behavior.

Any thoughts?