Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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>?

like image 299
shujin Avatar asked Dec 01 '25 02:12

shujin


2 Answers

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.

like image 199
dkatzel Avatar answered Dec 03 '25 14:12

dkatzel


Generics are sometimes a bit difficult. Try to analyze it step by step.

Imagine - in your first example (that doesn't compile) - you call the put method with the type variable E replaced by Number. As you are constraining the input parameter list with List<? extends E> it could be a List<Integer> for example. The parameter value is of type E (remember: E is Number), so it could be a Double for example. And this must not be allowed because you would try to add a Double into a List<Integer>.

In the second example you only have a constraint on the input parameter list. No other parameter is dependent on E. Setting and getting on a list will always work because the elements in question must be of the same type.

A solution:

Always remind the acronym PECS. See Wildcard (Java). That means you should declare the put method as following (in your first example):

public <E> void put(List<? super E> list, int toPos, E value) {
    putHelper(list, toPos, value);
}
like image 26
Seelenvirtuose Avatar answered Dec 03 '25 15:12

Seelenvirtuose



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!