On a generic class of type T, how to find E if T is List<E>

Viewed 669

I have a generic class parameterized with T. It holds data of type T and has a few methods using type T. However, if T happens to be a List<E>, I'd like to be able to write a method where I can use this E as a type, but I'm not sure if there's a way to do that.

As an example, consider this:

class Test<T> {
  private T data;
  public void setData(T t) {data = t;}
  public T getData() { return data; }
}

Now imagine this is instantiated as Test<List<String>>. So we have an object with a getData method returning a List<String>. On this object I would like to also have a method like setCallback taking something like Callback<String>. In other words, I need a method taking as its argument an interface parameterized with E where E relates to T as in List<E> == T.

How can I write this method?

Note that I do not want to force T into being a List by e.g. declaring T as List<E> on a class level, it must be possible to use the class both Test<Integer> and Test<List<String>>, but only in the latter case should the setCallback method make sense.

The closest thing I've come up with is this:

interface AbstractCallback<T> {}

interface Callback<E> extends AbstractCallback<List<E>> {
  void onSomethingHappened(E e);
}

class Test<T> {
  ...
  void setCallback(AbstractCallback<T> callback);
}

On this, I can do

Test<List<String>> test = new Test<>();
test.setCallback(new Callback<String>() {
  public void onSomethingHappened(String e) {}
});

This works, and if T is not a List I won't be able to create a fitting Callback, as desired (since it's simply not possible to create an instance of Callback that implements AbstractCallback<T> unless T is a List). However, being forced to have the setCallback method take the abstract super-class (interface actually) rather than the more concrete one makes the interface unclear and somewhat ambigious.

I have also considered placing setCallback on a sub-class of Test such that class SubTest<E> extends Test<List<E>> but this means I have to instantiate this class instead of Test, and even if I delegate construction of Test objects to a factory, the type of T is not known at time of construction (unless I add Class arguments to carry type information, which I'd rather not) - it will only be known after setData has been called. Also, I prefer if clients only have to refer to Test and not bother with down-casting etc.

Finally, I have considered just letting the client be responsible for supplying the correct type by just definining E as an unbound generic type unrelated to T, and simply let incorrect calls result in ClassCastExceptions:

<E> void setCallback(Callback<E> callback);

Is there a better way of doing this? To re-cap, basically I want to declare a generic method with a generic type E related to the class' generic type T so that T is equivalent to List<E>; and, ideally, if T is not a List at all, the method is not possible to invoke. So you could say this is the opposite of having a class of E and declaring a method with List<E> or even <T extends List<E>>. I imagine it may not be possible since at compile-time there is no way to tell if T is a List or not, but maybe there is a more clever way around it than my suggestion above?

2 Answers
Related