I develop some big project. Some parts of my code:
1)
private final ObjectPool<ProcessorMechanicsRoom> processorsPool;
... new ProcessorMechanicsRoom(processorsPool);
public class ProcessorMechanicsRoom
extends ProcessorMechanics<ProcessMechanicsRoom, IMechanicsRoom, IMechanicsRoomCallback> {
...
public ProcessorMechanicsRoom(ObjectPool<ProcessorMechanicsRoom> pool) {
}
super(pool); // the problem is here
}
public class ProcessorMechanics
<P extends ProcessMechanics<M,C>, M extends IAMechanics<C>, C extends IAMechanicsCallback>
extends Processor<P> {
private final ObjectPool<ProcessorMechanics<P,M,C>> pool;
...
public ProcessorMechanics(ObjectPool<ProcessorMechanics<P,M,C>> pool) {...}
...
}
The problem is that ObjectPool<ProcessorMechanicsRoom> cannot be passed into super-constructor (code 2). So i am confused.
There's this thing called variance.
Let's use some types we are all familiar with:
java.lang.Integer extends java.lang.Number extends java.lang.Object
In a covariant system, you can write:
Number x = new Integer();
but you cannot write:
Integer y = new Number();
As you might surmise, basic assignment and such in java is all covariant. But that's not the only way to do it.
In a contravariant system, you cannot write:
Number x = new Integer();
but on the flipside, this actually works:
Integer y = new Number();
This is the inflexible one; in this one, neither works. The only thing you can do is:
Integer y = new Integer();
Whereas java is covariant for basic stuff, generics isn't. Generics is contravariant, or covariant, or invariant, depending on how you write the generics.
List<? extends Number> list = new ArrayList<Integer>(); // legal
List<? super Integer> list = new ArrayList<Number>(); // legal
List<Integer> list = new ArrayList<Integer>(); // only integer will do here
You've picked invariant. So only ProcessorMechanics
will do; your ProcessorMechanicsRoom
is a subclass, and therefore you can't do that unless your type relationship allows covariance, and it does not. Make that ? extends
and it'll work.
Because... life. That is how real life works.
Imagine it did not. I can do this, then, and break everything:
List<Integer> ints = new ArrayList<Integer>();
List<Number> numbers = ints; // MARK THIS LINE!
numbers.add(new Double(5.0));
Integer x = ints.get(0); // ERROR!
In the above, if it had compiled and run, the last line would be an error, as the .get(0) call would retrieve a double value which isn't an integer. Fortunately, the above does not compile; the error occurs on the marked line. That's.. because the compiler should disallow this. Generics by its very nature are invariant.
Now, covariance can exist. For example, if you have a method that will sum up the result of invoking .intValue()
on each of the Numbers inside, then you could write:
public int sumAll(List<Number> list) {
int result = 0;
for (Number n : list) result += n.intValue();
return result;
}
but that's a bad way to write it; you've decreed that the parameter is invariant, thus, you cannot pass a List<Integer>
to this thing. But the code is covariant. It would work just as well if you pass a list of integers. So, you should write that as public int sumAll(List<? extends Number> numbers)
instead.
Here is an example of invariance:
public void addSumToEnd(List<Number> list) {
int sum = 0;
for (Number n : list) sum += n.intValue();
list.add(sum);
}
Because we're adding a number here, you couldn't write List<? extends Number>
. After all, we're adding an int
and you can't do that to a List<Double>
. The only acceptable lists you can feed in here are List<Number>
and List<Integer>
and there's no way to express that in java.
For lists, it's easy: "contravariance = adds" (.add()
, .addAll()
, etc), "covariance = reads", "invariance = does both". For other generified types it may not be that simple.
Presumably if your ProcessorMechanics class will only ever 'read', then you can make it covariant, and write:
public ProcessorMechanics(ObjectPool<? extends ProcessorMechanics<P, M, C>> pool) {...}