Skip to content
Advertisement

How to return the same generic Collection-type with different element-type?

I have a method, that maps elements of a collection to other object and returns a collection containing the mapped elements. I want the returned Collection to be of the same Collection-type as the input Collection, but with a different element type. My method would look something like this:

<E extends OriginalElement, T extends TargetElement,
 C extends Collection<E>, R extends C<T>> R map(C elementsToMap) {
    // snip
}

Obviously the part R extends C<T> doesn’t work.

How can I specify that return type to be the same subclass of Collection as Type C, but with element type T instead of E?

Advertisement

Answer

You can’t, I don’t think, because e.g. ArrayList<String> and ArrayList<Integer> are essentially unrelated types.

Plus, when you say “same generic Collection-type”, do you mean:

  • “if I give you some subclass of ArrayList, you’ll give me back an instance of java.util.ArrayList”; or
  • “if I give you a specific subclass of ArrayList, you’ll give me back an instance of the same specific subclass of ArrayList”?

Not only is that hard, because in general you don’t know how to instantiate arbitrary subclasses, you might not be able to create such an instance, for example if the input is an IntegerArrayList (extends ArrayList<Integer>), and you want to map the elements to Strings. So, whilst you could return a java.util.ArrayList<String> in that case, you can’t have a generic solution because you need some knowledge of “which type to instantiate in this specific case”.


I am going to make an unquantified assertion that a small handful of collection types can handle most cases. So, provide overloads for these specific types:

</* type variables */> ArrayList<T> map(ArrayList<E> input) { ... }

</* type variables */> HashSet<T> map(HashSet<E> input) { ... }

</* type variables */> ImmutableList<T> map(ImmutableList<E> input) { ... }

// etc.

and then provide a general method for the other cases, and leave it up to callers to specify the collection type they want:

</* type variables */> Stream<T> map(Collection<E> input) { ... }

and then call the general method from the specific methods:

</* type variables */> ArrayList<T> map(ArrayList<E> input) {
  return map((Collection<E>) input).collect(toCollection(ArrayList::new));
}

// etc.
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement