Search code examples
javagenericsbounded-wildcardunbounded-wildcard

Generics in Java - Wildcard Usecases


We use wildcard in the method args when we want to pass list containing objects of child class. But as shown below we can achieve the same functionality using Type parameter. So why we need wildcards ?

Scenario
Lets say we have base-class named Department and its sub-classes named Development & Sales. Development & Sales are subtypes of Department but List & List are not subtypes of List.
So when we want to pass list of Development or Sales object as method arg which is accepting list of any type of Department we use wildcard. But inside the code we can see we can achieve the same without using wildcard.

As per Effective Java using wildcard in return type is really a bad choice. What are the other usecases where wildcard is really helpful?

class MyGenericsClass<T extends SuperClass> {

    //These 2 methods works the same
    <H> void unboundedMethod1(AnotherGenericClass<H> arg) {
    }
    void unboundedMethod2(AnotherGenericClass<?> arg) {
    }

    //These two methods works the same
    <H extends SuperClass> void boundedMethod1(AnotherGenericClass<H> arg) {
    }   
    void boundedMethod2(AnotherGenericClass<? extends SuperClass> arg) {
    }   

    //These two methods works the same
    <U extends Building> void boundedListMethod1(List<U> list){
    }
    void boundedListMethod2(List<? extends Building> list) {
    }
    
    //Why can't we write like this? Giving Compile time error
    //<U> void notWorkingMethod1(List<U extends SuperClass> list){ //Statements }
    //<T> void notWorkingMethod2(List<U extends SuperClass> list){ //Statements }
}

Inside the notWorkingMethods1 and notWorkingMethods2 why can't we pass Bounded Type parameter directly but we can do so by first declaring it before return type ?


Solution

  • First, your initial assumption that the two methods behave the same is incorrect.

    Assume the generic classes were Lists. Try adding something of type H to List<H> and anything to List<?> and see if they behave the same.

    Regarding the last question.

    <U extends Building> void boundedListMethod1(List<U> list)
    

    Says that U is a type that extends Building and List contains that type.

    However,

    <U> void notWorkingMethod1(List<U extends Building> list)
    

    Says that there is some type U and a List that expects a type U that extends Building. Those two statements do not imply compatibility. U may not be a subClass of Building but the List expects it.

    WildCards are helpful when you just want to do something without regard to type.

    List<Map<String,Integer>> list = ....
    
    for (Map<?,?> m : list) {
       System.out.println(m);
    }
    

    They are also useful for copying types.

    public <H> void copy(List<? extends H> src, List<? super H> dst) {
       for (H a : src) {
           dst.add(a);
       }
    }