1

I have a class who relies on generics to bind types internally.

class C<T1, T2 extends T1> {
    T1 e1;
    T2 e2;
    /* stuff */
}

In my code I know that, while c.e1 and c.e2 might not have the same class, c.e2 is an instance of a subclass of the class of c.e1.

I need to create a List of these cs, and those elements would not share a common base class. Still, I want to preserve the fact that each element of the List has the attributes bounded between each other.

Ideally I would like to create a List<C<?, ? extends ...>> "but" I don't know any syntax to specify this behaviour. And using a List<C<?, ?>> it seems to me I lost the generics types.

What is the correct way to do this collection?

Vito De Tullio
  • 2,332
  • 3
  • 33
  • 52

2 Answers2

2

Since T2 is bounded in the declaration of C, you will always retain the fact that it is a subtype of T1 even if you use wildcards. i.e. it is a characteristic of C that the second type parameter extends the first.

So you can just use List<C<?, ?>>.

You can already see that this works when you try to bind the generic parameters to type variables. e.g.:

public static <T1, T2> void m(List<C<T1, T2>> list) { ... }
                                         ^^-- Bound mismatch

You would need to use:

public static <T1, T2 extends T1> void m(List<C<T1, T2>> list) {...}

Or:

public static <T1> void m(List<C<T1, ? extends T1>> list) {...}

For the compiler to be happy.

Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93
  • Unfortunately the List elements are not omogeneous; I mean I need to put both a `c1 = new C() ` and `c2 = new C() `, but there is no base class between `Iface1` and `Iface2` nor between `Impl1` and `Impl2`. So I cannot have a `List>` because there is no `T1` or `T2` to begin with – Vito De Tullio Jan 29 '18 at 17:32
  • @VitoDeTullio That was just an example, the point is that you can just use `List>`. – Jorn Vernee Jan 29 '18 at 17:40
  • The problem is tha if I use `List>` i lose the types. - I have another method ` void m(T1 e1, T2 e2)` and I cannot extract the elements from the list and split them and pass them to the method – Vito De Tullio Jan 30 '18 at 09:20
  • @VitoDeTullio Maybe I'm misunderstanding your question then. It would help if you gave more of an example of how you want to use the list... But if you don't want to lose the types _at all_ you can use 2 lists, 1 is a `List` and the other a `List>`. – Jorn Vernee Jan 30 '18 at 10:03
  • The problem is I have an (potentially) indefinite number of types. At the moment there are 7 interfaces and 10 implementations. Having a list for each combination defies the purpose of the list itself – Vito De Tullio Feb 05 '18 at 10:25
0

I find a solution encoding the types constraint in a custom (un-generic) type.

so now I have these classes:

class MainLogic {
    List<TypeHelper<?,?>> elements = new ArrayList<>();
    <T1, T2 extends T1> void addElement(T1 e1, T2 e2) {
        this.elements.add(new TypeHelper<T1,T2>(e1, e2));
    }
    void logic() {
        for (TypeHelper<?, ?> element: elements)
            element.logicHelper(this);
    }
    <T1, T2 extends T1> void logicHelper(T1 e1, T2 e2) {
        // here finally I have the variables constrained
    }
}

class TypeHelper<T1, T2 extends T1> {
    T1 e1;
    T2 e2;
    void logicHelper(MainLogic ml) {
        ml.logicHelper(e1, e1);
    }
}
Vito De Tullio
  • 2,332
  • 3
  • 33
  • 52
  • But you don't need `logicHelper` in `TypeHelper` here, you can just call `this.logicHelper(element.e1, element.e2)` and it works. See here: https://ideone.com/h4un0O So if `TypeHelper` is `C` here, the type of `e1` will always extend the type of `e2`. – Jorn Vernee Feb 05 '18 at 11:29