Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic methods. Multiple arguments match

Tags:

java

generics

I cant figure out why does this code work without error.

static <T> int compare(T t1, T t2) {
    return 0;
}

public static void main(String[] args) {
    compare(new Thread(), new StringBuilder());
}

Because when we have something like this:

static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
    return;
}

public static void main(String[] args) {
    fromArrayToCollection(new Thread[] {}, new ArrayList<StringBuilder>()); // error
}

We have an error. Why doesn't compiler check match of both arguments in first example? Sorry for stupid question.

like image 244
sedni Avatar asked Mar 05 '23 03:03

sedni


2 Answers

Generics are invariant

Generics are invariant, not covariant.

This means that, while you can do something like this:

Dog dog = new Dog();
Animal animal = dog; // Dog works for Animal, classes are covariant

You can not do this:

List<Dog> dogs = List.of(new Dog());
List<Animal> animals = dogs; // Error, generics are invariant

So List<Dog> is not of type List<Animal>. But Dog is of type Animal.

Of course this makes sense, since animals would accept Cats, but dogs not.


Explanation

In your first code, you did not specify the type to use, such as:

compare<Foo>(...)

so you let the compiler deduce the type. It searches a type which Thread and StringBuilder have in common, which is Object.

So T resolves to Object there, which is fine:

static int compare(Object t1, Object t2) {
    return 0;
}

In your second example it can not pick Object, because Collection<Object> behaves different to Collection<StringBuilder>, since generics are invariant. So it is unable to find a type that matches, thus an error occurs.

Also see Why are arrays covariant but generics are invariant?.

like image 106
Zabuzard Avatar answered Mar 16 '23 10:03

Zabuzard


I'd like to add that a simple change in the second code snippet would correct the error:

static <T> void fromArrayToCollection(T[] a, Collection<? extends T> c) { ... }
                                                             ^

Now, the Java compiler will infer the Object again:

fromArrayToCollection(new Thread[]{}, new ArrayList<StringBuilder>());

Also, you can add the type argument explicitly:

Main.<Object>fromArrayToCollection(new Thread[]{}, new ArrayList<StringBuilder>());
        ^

T => Object:

static void fromArrayToCollection(Object[] a, Collection<? extends Object> c) { ... }

Further reading: Upper Bounded Wildcards.

like image 22
Oleksandr Pyrohov Avatar answered Mar 16 '23 10:03

Oleksandr Pyrohov