3

I was going through this article to understand the role of super wildcard in generics. I understood how extends works, but I am having difficulty understanding super

I have a ClassA that is extended by ClassB, making ClassA super class of ClassB.

If I understood the article correctly, the <? super ClassB> would allow any Class that is a super type of ClassB.

I have the following code

GenericMethod.java

public class GenericMethod<T> {

    private List<T> list;

    public GenericMethod() {
        list = new ArrayList<>();
    }

    public void add(T t) {
        list.add(t);
    }

    public T get(int index) {
        return list.get(index);
    }
}

Driver.java

public class Driver {

    public static void main(String[] args) {

        GenericMethod<? super ClassB> genericMethod = new GenericMethod<>();

        ClassA classA = new ClassA();
        genericMethod.add(classA); // Compile-time error here

    }

}

Error

The method add(capture#1-of ? super ClassB) in the type GenericMethod<capture#1-of ? super ClassB> is not applicable for the arguments (ClassA)

I don't understand where I am going wrong. When I instantiated the GenericMethod class, I already declared that it would accept any value that is a super type of ClassB with the declaration <? super ClassB>. Thus, the T inside the GenericMethod class should accept all classes that ClassB extends.

Why does the add method throw the compile-time error then? Shouldn't the method add already know that it's being passed a perfectly compatible type?

Auro
  • 1,578
  • 3
  • 31
  • 62

2 Answers2

1

The ? super clause is a lower-bound wildcard. But the bound is on the type parameter that is inferred, not a restriction on the types of arguments that can be passed to a method that takes a parameter of that generic type.

When you say <? super ClassB>, you indicate that the type parameter can be ClassB or any supertype, e.g. ClassA or Object.

The compiler must treat the add method as if it could be any of these signatures:

add(Object t)
add(ClassA t)
add(ClassB t)

(There could be other types if ClassA inherited directly from another class instead of Object).

The compiler must reject a ClassA as an argument to add because the type parameter could be inferred as ClassB. It's legal to assign a GenericMethod<ClassB> to your genericMethod variable.

GenericMethod<? super ClassB> genericMethod = new GenericMethod<ClassB>();

But it doesn't make sense to be able to pass a ClassA to a method that expects a ClassB.

In fact that is what is inferred by the diamond operator - ClassB.

Your confusion is in the conflation of two concepts: what type parameters are allowed and what types of objects are allowed in methods that use a type parameter. Using a wildcard restricts the type parameter, but the method still accepts types that are the type parameter or a subtype.

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • Could you please elaborate what you meant by **The compiler must reject a ClassA as an argument to add because the type parameter could be inferred as ClassB**? – Auro Oct 17 '19 at 18:43
  • 1
    I am not understanding the point of using a lower-bound type parameter if the method in the class is going to accept only those that are of lower-bound class or subtype of that. – Auro Oct 17 '19 at 18:49
  • Please see [What is PECS?](https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super). A method that takes a parameter typed as a generic type parameter, which is in turn defined with `? super` is guaranteed to be able to accept an argument typed as the lower bound or lower. Here, the `add` method is only guaranteed to be able to accept an argument of type `ClassB` or lower, not matter what the actual type parameter is. – rgettman Oct 17 '19 at 19:07
1

By declaring GenericMethod<? super ClassB> you are declaring the type is an unknown type that is a super-class of ClassB (or ClassB itself). And asking the compiler to only allow subtypes of this unknown type to be added to the list.

The only compatible subtypes the compiler knows are ClassB and any subtypes of ClassB. When creating instances, it is usually better to avoid wildcards.

For method parameters, wildcards give you more flexibility on what can be accepted. Use PECS (producer=extends, consumer=super) to determine which to use. See Slide 5

boot-and-bonnet
  • 731
  • 2
  • 5
  • 16