Paul Marcelin Bejan
Paul Marcelin Bejan

Reputation: 1675

Compilation error passing wildcards to generics method

I would like to understand why it is not possible to pass wildcards to generics, and what is the best solution for that.

I will use a simple reproducible example, but the code is more complex.

I have a method that takes as a parameter a map of Class<GENERIC_TYPE> as key and List<GENERIC_TYPE> as value.

private <CAR extends Car> void doSomething(final Map<Class<CAR>, List<CAR>> carMap) {
    carMap.entrySet().stream().forEach(entry -> {
        System.out.println("\n" + entry.getKey().getSimpleName() + "\n" + entry.getValue() + "\n");
    });
}

The method that is in charge of passing the map to the method "doSomething" doesn't have generics (and I cannot add bounded generics because is overriding a method of an interface)

so the Map is declared using wildcards:

@Override
public void doSomethingPowerfull() {
    Bmw bmwM1 = new Bmw("BMW", "M1");
    Bmw bmwM3 = new Bmw("BMW", "M3");
    Bmw bmwM5 = new Bmw("BMW", "M5");
    List<Bmw> bmw = List.of(bmwM1, bmwM3, bmwM5);

    Audi audiS1 = new Audi("Audi", "S1");
    Audi audiS3 = new Audi("Audi", "S3");
    Audi audiS5 = new Audi("Audi", "S5");
    List<Audi> audi = List.of(audiS1, audiS3, audiS5);

    Map<Class<? extends Car>, List<? extends Car>> carMap = new HashMap<>();
    carMap.put(Audi.class, audi);
    carMap.put(Bmw.class, bmw);

    doSomething(carMap); // COMPILATION ERROR
    // The method doSomething(Map<Class<CAR>,List<CAR>>) in the type TestWildCardGenerics is not applicable for the arguments (Map<Class<? extends TestWildCardGenerics.Car>,List<? extends TestWildCardGenerics.Car>>)
}

They extends the same class, so why I'm not able to pass this map?

Here is the complete test class:

public class TestWildCardGenerics {

    public void test() {
        Bmw bmwM1 = new Bmw("M1");
        Bmw bmwM3 = new Bmw("M3");
        Bmw bmwM5 = new Bmw("M5");
        List<Bmw> bmw = List.of(bmwM1, bmwM3, bmwM5);

        Audi audiS1 = new Audi("S1");
        Audi audiS3 = new Audi("S3");
        Audi audiS5 = new Audi("S5");
        List<Audi> audi = List.of(audiS1, audiS3, audiS5);

        Map<Class<? extends Car>, List<? extends Car>> carMap = new HashMap<>();
        carMap.put(Audi.class, audi);
        carMap.put(Bmw.class, bmw);

        doSomething(carMap);
    }

    private <CAR extends Car> void doSomething(final Map<Class<CAR>, List<CAR>> carMap) {
        carMap.entrySet().stream().forEach(entry -> {
            System.out.println("\n" + entry.getKey().getSimpleName() + "\n" + entry.getValue() + "\n");
        });
    }

    @Data
    @AllArgsConstructor
    public abstract class Car {
        protected String name;
    }

    @Data
    @ToString(callSuper = true)
    public class Audi extends Car {
        private String sModel;

        public Audi(final String sModel) {
            super("Audi");
            this.sModel = sModel;
        }
    }

    @Data
    @ToString(callSuper = true)
    public class Bmw extends Car {
        private String mModel;

        public Bmw(final String mModel) {
            super("BMW");
            this.mModel = mModel;
        }
    }

}

Upvotes: 0

Views: 36

Answers (1)

Andy Turner
Andy Turner

Reputation: 140326

They extends the same class

No, each occurrence of ? extends Car is considered to be a different type, because they could be different types. The signature of doSomething requires them to be the same.

Unless there's a good reason for doSomething to require both Class and List to be bounded by the exact same type, you could simply use a wildcard there:

private void doSomething(final Map<Class<? extends Car>, List<? extends Car>> carMap) {

Upvotes: 1

Related Questions