Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Set<? extends Foo<?>> allowed, but Set<Foo<?>> is not [duplicate]

Tags:

I want to know how generics work in this kind of situation and why Set<? extends Foo<?>> set3 = set1; is allowed but Set<Foo<?>> set2 = set1; is not?

import java.util.HashSet;
import java.util.Set;

public class TestGenerics {
    public static <T> void test() {
        Set<T> set1 = new HashSet<>();
        Set<?> set2 = set1;             // OK
    }

    public static <T> void test2() {
        Set<Foo<T>> set1 = new HashSet<>();
        Set<Foo<?>> set2 = set1;           // COMPILATION ERROR
        Set<? extends Foo<?>> set3 = set1; // OK
    }
}

class Foo<T> {}
like image 344
Stoyan Radnev Avatar asked Jan 09 '19 08:01

Stoyan Radnev


People also ask

What is the difference between List <? Super T and List <? Extends T?

extends Number> represents a list of Number or its sub-types such as Integer and Double. Lower Bounded Wildcards: List<? super Integer> represents a list of Integer or its super-types Number and Object.

What is difference between extends and super in Java?

super is a lower bound, and extends is an upper bound.

What does Super T means in Java?

super T denotes an unknown type that is a supertype of T (or T itself; remember that the supertype relation is reflexive). It is the dual of the bounded wildcards we've been using, where we use ? extends T to denote an unknown type that is a subtype of T .


2 Answers

Simply said, this is because Set<? extends Foo<?>> is covariant (with the extends keyword). Covariant types are read-only and the compiler will refuse any write action, like Set.add(..).

Set<Foo<?>> is not covariant. It does not block write or read actions.

This...

Set<Foo<String>> set1 = new HashSet<>();
Set<Foo<?>> set2 = set1; // KO by compiler

... is illegal because otherwise I could for example put a Foo<Integer> into set1 via set2.

set2.add(new Foo<Integer>()); // Whoopsie

But...

Set<Foo<String>> set1 = new HashSet<>();
Set<? extends Foo<?>> set3 = set1; // OK

... is covariant (extends keyword), so it is legal. For example, the compiler will refuse a write operation like set3.add(new Foo<Integer>()), but accept a read operation like set3.iterator().

Iterator<Foo<String>> fooIterator = set3.iterator(); // OK
set3.add(new Foo<String>()); // KO by compiler

See these posts for a better explanation:

  • https://stackoverflow.com/a/4343547/7709086
  • https://medium.freecodecamp.org/understanding-java-generic-types-covariance-and-contravariance-88f4c19763d2
like image 174
kagmole Avatar answered Oct 09 '22 12:10

kagmole


Perhaps the issue becomes clearer if you leave the generic parameter of Foo out of the equation.

Consider

final Set<Foo> set1 = new HashSet<>();
Set<Object> set2 = set1;

This makes the compile error more obvious. If this was valid, it would be possible to insert an object into set2, thus into set1 violating the type constraint.

Set<? extends Foo> set3 = set1;

This is perfectly valid because set1 would also accept types derived from Foo.

like image 41
leftbit Avatar answered Oct 09 '22 10:10

leftbit