0

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.

  • 1
    Edit your question and show the full text of the compilation error. On a design note, having both ProcessMechanics and ProcessorMechanics as class names, as well as ProcessMechanicsRoom and ProcessorMechanicsRoom, is quite confusing. I may try replacing those names in my own private copy of your code, to clarify the problem, but for now, it’s so hard to tell them apart that I gave up on trying. You will get more help if your code is easier to understand. – VGR Aug 25 '20 at 17:18
  • logically, ProcessorMechanicsRoom can be casted as ProcessorMechanics<...>. But my ide (intellij) display it as error. Is so logically! – pro100kryto Aug 25 '20 at 17:21

1 Answers1

2

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

Covariance

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.

Contravariance

In a contravariant system, you cannot write:

Number x = new Integer();

but on the flipside, this actually works:

Integer y = new Number();

Invariance

This is the inflexible one; in this one, neither works. The only thing you can do is:

Integer y = new Integer();

Okay, so, what about generics?

Whereas java is covariant for basic stuff, generics isn't. Generics is contravariant, or covariant, or invariant, depending on how you write the generics.

  • Covariant: List<? extends Number> list = new ArrayList<Integer>(); // legal
  • Contravariant: List<? super Integer> list = new ArrayList<Number>(); // legal
  • Invariant: 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.

Um, wtf? Why???

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) {...}
rzwitserloot
  • 85,357
  • 5
  • 51
  • 72