-1

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!

Markus H
  • 13
  • 3
  • 3
    Because in the line `GenericTest(Super s){` you are using the [raw type](http://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it) `Super`. – Jesper Oct 27 '16 at 07:09
  • You should probably use GenericTest(Super s) I just paste your code in eclipse, and the error is pretty straightforward... GenericTest.Super is a raw type. References to generic type GenericTest.Super should be parameterized – Cédric O. Oct 27 '16 at 07:09
  • Ok, thank you, works for this example. But Maybe it's too simple. In my original use case I can't just use `Exception`, because I don't know what subtype I get. In fact I want to replace List elements. So when I make the constuctor argument `Super extends Exception> s` it works. But when I replace the constructor code by `s.set(0,s.foo().get(0));` I still get an error – Markus H Oct 27 '16 at 07:27

2 Answers2

3

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

UPDATE 2

Why does convertException not know, that it will return the same type as the list has?

The problem is not that the compiler doesn't know that "F extends Exception == ? extends Exception". This piece of code:

GenericTest(Super<Exception> s){
        List<? extends Exception> list = getList();

    }

    <F extends Exception> List<F> getList(){...}

will compile. The problem is that you're using the set method on collections with wildcard, wich is forbidden, no matter what object are you actually passing.

Svech87
  • 88
  • 1
  • 1
  • 7
  • Or maybe `GenericTest(Sub1 s)`. – walen Oct 27 '16 at 07:21
  • Thank you, but I don't know which subtype of Exception I will get. And even at Runtime I don't know it. Using ` extends Exception>` isn't enough too, because I need to call foo() two times and the resuts have to have the same generic type – Markus H Oct 27 '16 at 07:45
  • Thanks, that helps! – Markus H Oct 27 '16 at 09:30
  • Just for understanding: IfI initialize my list with `List list = new ArrayList` and call `convertException(list.get(0))`, this method gets the correct generic type (`NullPointerException`). If I initialize the list with `List extends Exception> list = new ArrayList`, and make the same call: Why does `convertException` not know, that it will return the same type as the list has? At runtime it will be `NullPointerException`, but at compile-time it should know, that it will be the same `capture#x-of ? extends Exception`. – Markus H Oct 27 '16 at 09:30
1

You can use casting. Object is a superclass https://docs.oracle.com/javase/tutorial/java/IandI/objectclass.html

    Exception e =  (Exception) s.foo().get(0);

I think this is what you were aiming for?

    List<NullPointerException> ds = new GenericTest.Sub1().foo();
    Exception e2 = ds.get(0);
Raul
  • 51
  • 3