Markus H Markus H - 1 month ago 7
Java Question

Why does my generic function return different captures as it gets?

I have a superclass with a generic type extending a supertype (

<E extends ...>
). This class has an abstract method that returns a list of the generic type.

Also I have a few subclasses implementing the abstract method.

When I call this method and try to replace objects in the list, the java compiler shows an error. I think the error is because my converting function returns a different capture of the type as it gets as parameter.

Here is a sample code using Exception as generic supertype:


import java.util.ArrayList;
import java.util.List;

public class GenericTest {

abstract class Super<E extends Exception>{
abstract List<E> foo();
}

class Sub1 extends Super<NullPointerException>{
@Override
List<NullPointerException> foo(){
return new ArrayList<NullPointerException>();
}
}

GenericTest(Super<? extends Exception> s){
List<? extends Exception> list = s.foo();
list.set(0, convertException(list.get(0)));
}

static <F extends Exception> F convertException(F exception){...}
}




There are two error occurs in the line

list.set(0, convertException(list.get(0)));


The compiler says for
set
:

The method set(int, capture#2-of ? extends Exception) in the type List<capture#2-of ? extends Exception> is not applicable for the arguments (int, capture#3-of ? extends Exception)


and for
convertException
:

Type mismatch: cannot convert from capture#3-of ? extends Exception to capture#2-of ? extends Exception


Why doesn't convertEException return the same
capture#x
as it gets? It takes F and returns F.

Thanks for your help in advance!

Answer

This is because you are passing Super to the constructor as a raw type. You are not using the generic. Since you don't specify the generic type, the compiler considers the list as a list of Object.

It should be like this:

GenericTest(Super<Exception> s){
        Exception e = s.foo().get(0);
    }

This will compile fine

UPDATE

The compiler says for set

The method set(int, capture#2-of ? extends Exception) in the type List is not applicable for the arguments (int, capture#3-of ? extends Exception)

Java doesn't allow you to add or update elements of a Collection when you're using wildcard. So, using:

List<? extends Exception> list = s.foo();
        list.set(0, /*anything but null*/);

is forbidden. The reason is to avoid this situation:

class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}

public void method() {

        List<Dog> dogs = new ArrayList<Dog>();

        addCats(dogs);
    }

    void addCats(List<? extends Animal> list) {
        list.add(new Cat());
    }

You see the point? If adding operation were allowed, you would risk to add cats in an list of dogs.

Back to your problem. I don't understand precisely what you have to do, if you really need a list with a specific subtype of exception, I suggest you to make also GenericTest as a generic class . Otherwise you can declare your list as a simple list of Exception:

GenericTest(Super<Exception> s){
        List<Exception> list = s.foo();
        list.set(0, convertException(list.get(0)));
    }

and then make instanceof checks on your list elements.

Hope this helps

Comments