Home > Enterprise >  Java Covariance, Invariance and Contravariance
Java Covariance, Invariance and Contravariance

Time:05-17

I read some articles about Covariance, Contravariance, and Invariance in Java, I'm confused trying to understand this :(

I am using java 11, and I have a class hierarchy A :> B :> C (means that C is a subtype of B and A, and B is a subtype of A) and a class Container:

class Container<T> {
    public final T t;
    public Container(T t) {
        this.t = t;
    }
}

for example, if I define a function:

public Container<B> method(Container<B> param){
  ...
}

here is my confusion, why does the third line compile?

method(new Container<>(new A())); // ERROR
method(new Container<>(new B())); // OK
method(new Container<>(new C())); // OK Why ?, I make a correction, this compiles OK

if in Java Generics are invariant.

when I define something like this:

Container<B> conta =  new Container<>(new A()); // ERROR, Its OK!
Container<B> contb =  new Container<>(new B()); // OK, Its OK!
Container<B> contc =  new Container<>(new C()); // Ok, why ? It's not valid, because they are invariant

can you please help me understand this?

Thanks!

CodePudding user response:

Covariance is the ability to pass or specify a subtype when a supertype is expetced. If your C class extends B, then C is a child class of B. This relationship between C and B is also called is-a relationship, where an instance of C is also an instance of B. Therefore when your variable contc is expecting a B instance and you're passing new C(), since new C() is an instance of C and C instance is (also)-an instance of B, then the compiler allows the following writing:

Container<B> contc = new Container<>(new C());

Conversely, when you're writing

Container<B> conta = new Container<>(new A());

you're receiving an error because A is a supertype of B, there is no is-a relationship from A to B, but rather from B to A. This is because every instance of B is also an instance of A, but not every instance of A is an instance of B (To make a silly example, every thumb is a finger but not every finger is a thumb). A is a generalization of B; therefore it cannot appear where a B instance is expected.

Here there's a good article expanding the concept of covariance in java.

https://www.baeldung.com/java-covariant-return-type

CodePudding user response:

The question's examples don't demonstrate the invariance of generics.

An example which does demonstrate this would be:

ArrayList<Object> ao = new ArrayList<String>(); // does not compile

(You might incorrectly expect the above to compile, because String is a subclass of Object.)

The question shows us different ways to construct Container<B> objects - some of which compile and others which do not, because of the inheritance hierarchy of A, B and C.

That diamond operator <> means that the created container is of type B in every case.

If you take the following example:

Container<B> contc =  new Container<>(new C()); // compiles

And re-write it by populating the diamond with C, the you will see that the following does not compile:

Container<B> contc =  new Container<C>(new C()); // does not compile

That will give you the same "incompatible types" compilation error as my ArrayList example.

  • Related