Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - Collection of generic classes, each with a different generic type

Tags:

java

generics

Suppose you have the following:

@AllArgsConstructor
public class Foo<A> {
  A a;
}

and you have the following:

Foo<String> fooString = new Foo<>("abc");
Foo<Integer> fooInteger = new Foo<>(10);

List<Foo> foos = new ArrayList<>();
foos.add(fooString);
foos.add(fooInteger);

And I want to loop over the list of foos

for (Foo foo : foos) {
    ...
}

First of all, is there a different way of accomplishing this?

Then, how do I reference the generic type of each foo object inside the list? I tried with a wildcard but I'm encountering a compilation error "capture#1-of-? ..."

like image 637
Diego Rincon Avatar asked Sep 05 '25 03:09

Diego Rincon


1 Answers

The following works because in your case you know that the foo types can only be either Integer or String and these are final classes assuming that Foo.a is never ```null``:

Foo<String> stringFoo = ...;
Foo<Integer> integerFoo = ...;

List<Foo<?>> allFoos = new ArrayList<>();
allFoos.add(stringFoo);
allFoos.add(integerFoo);

for (Foo<?> foo : allFoos) {
   if (foo.a instanceof Integer) {
      @SuppressWarnings("unchecked");
      Foo<Integer> intFoo = (Foo<Integer>) foo;
      //...
   } else if (foo.a instanceof String) {
      @SuppressWarnings("unchecked")
      Foo<String> strFoo = (Foo<String>) str;
      //...
   } else {
      throw new IllegalStateException("this cannot happen");
   }
} 

However in general this won't work if allFoos could be populated with arbitrary Foo types (not know in compilation-time) ... once you mix them all in the same bag/list you loose the ability to figure them out the type that was use in the instantiation code. You could assume that this is the same as the non-null Foo.a member instance but that is not necessarily the case:

// assuming constructor defined as Foo(A a) { this.a = a };
Foo<Number> one = new Foo<>((double) 100);
Foo<Double> two = new Foo<>(100);

Foo<?> oneUnknown = one;
Foo<?> twoUnknown = two;

You would perhaps end up saying that both are Foo<Double> but one was Foo<Number> when was constructed. In this case because Foo is very simple class it might not make a difference but if it had more complex operations perhaps it would.

like image 142
Valentin Ruano Avatar answered Sep 07 '25 19:09

Valentin Ruano