Search code examples
javagenericscompiler-errorsbounded-wildcardhelpermethods

Private helper method to capture wildcard type for generic methods


The following code does not compile in Eclipse. It says "The method putHelper(List,int,E) in the type Abc is not applicable for the arguments (List <.capture#8-of extends E>",int,E)"

private <E> void putHelper(List<E> list, int i, E value) {
    list.set(i, value);
}

public <E> void put(List<? extends E> list, int toPos, E value) {
    // list.set(toPos,value);
    putHelper(list, toPos, value);
}

I don't understand why it is so? Because the code below works fine.

  public <E> void put(List<? extends E> list,int fromPos, int toPos) {
  putHelper(list,fromPos,toPos);
  }

  private <E> void putHelper(List<E> list,int i, int j) {
  list.set(j,list.get(i));
  }

And I understand that here the helper method is able to capture the wildcard type, but why not in the earlier code?

EDIT: In the third case, if I change type parameter in the put method to List<.? super E> and when I try to call the put() method from another method which takes a list, Eclipse doesn't compile it. It says, "The method put(List<.? super E>,int,E) in the type Abc is not applicable for the arguments (List <.capture#6-of extends E>",int,E)"

public static <E> void insertAndProcess(List<? extends E> list) {

// Iterate through the list for some range of values i to j

    E value = list.get(i);

//Process the element and put it back at some index

    put(list, i+1, value);

//Repeat the same for few more elements
}

private static <E> void putHelper(List<E> list, int i, E value) {
    list.set(i, value);
}

public static <E> void put(List<? super E> list, int toPos, E value) {
    putHelper(list, toPos, value);
}

Here, how can insertAndProcess() call put() method and use it in its implementation, while the user can still be able to call both these methods with say ArrayList<.Integer>?


Solution

  • This is because of the Get and Put Principle also known by the acronym PECS which stands of Producer Extends, Consumer Super.

    This is explained in this SO question: What is PECS (Producer Extends Consumer Super)?

    But basically:

    public <E> void put(List<? extends E> list, int toPos, E value) {
       // list.set(toPos,value);
       putHelper(list, toPos, value);
    }
    

    <? extends E> can't be used here because the List is being used as a consumer (it is taking elements) so it should use super instead of extends.

    public <E> void put(List<? super E> list, int toPos, E value) {
       // list.set(toPos,value);
       putHelper(list, toPos, value);
    }
    

    EDIT

    In your 2nd case, List is acting as a producer because it is producing elements via the call to get() so you can use extends.

    However, in your 3rd example, you are both getting and putting into the same list so I don't think you can use wildcards at all.

    This compiles:

    public static <E> void insertAndProcess(List<E> list) {
    
        // Iterate through the list for some range of values i to j
        E value = list.get(i);
    
        // Process the element and put it back at some index
        putHelper(list, i+1, value);
    
        // Repeat the same for few more elements
    }
    

    Note that since we don't need to use any wildcards anyway because we are getting and setting from the same list so the type E must be the same no matter what it is.