Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Errors occur when calling print(List<T> a, T b) with different T class

Tags:

java

generics

I am trying to learn Java Generics, and found the following code.

public static <T> void print(T a, T b){
    System.out.println(a);
    System.out.println(b);
}

public static void main(String[] args){
    print(new ArrayList<String>(), 1);
}

Which works with no problem.

However when I change print method to the following, it gives me compiling errors.

public static <T> void print(List<T> a, T b){
    System.out.println(a);
    System.out.println(b);
}

Error:

GenericTest.java:9: error: method print in class GenericTest cannot be applied to given types;
  print(new ArrayList<String>(), 1);
    ^
  required: List<T>,T
  found: ArrayList<String>,int
  reason: no instance(s) of type variable(s) T exist so that argument type int conforms to formal parameter type T
  where T is a type-variable:
    T extends Object declared in method <T>print(List<T>,T)
1 error

Can anyone help me understand the errors?

like image 493
Xin Avatar asked Jan 22 '16 10:01

Xin


3 Answers

The first thing you should understand is that, with the following method signature

public static <T> void print(T a, T b)

Both T must be the same type, that is to say both a and b will have the same infered type.

So why does it work for new ArrayList<String>() and 1? Because both parameters can actually be represented as Serializable, which is the nearest common super type of ArrayList and Integer:

  • ArrayList implements the Serializable interface.
  • 1 can be boxed into an Integer, which is also Serializable.

So in this case, the compiler will infer T as Serializable.


In the second case, with the signature

public static <T> void print(List<T> a, T b)

There is no common super type T that would be valid for both List<String> and Integer. It is true that both String and Integer are Serializable, but since generics aren't polymorphic, it doesn't work.

like image 161
Tunaki Avatar answered Sep 25 '22 03:09

Tunaki


Edit: It doesn't get resolved to Object as I had mentioned but to Serializable. See Tunaki's correct answer for the reason.

In your first case, T is resolved to the most specific type which can refer to both your arguments.

And in your example, it will get resolved to Object Serializable, since ArrayList and Integer are both Object Serializable sub-types.

But in your second example, you have a List<T> as the parameter...

Now, when you call it with ArrayList<String>, the type T is resolved to a String. It cannot be resolved to any other type because an ArrayList<Object> ArrayList<Serializable> is NOT a supertype of ArrayList<String>

like image 42
Codebender Avatar answered Sep 25 '22 03:09

Codebender


You call the method in following ways :

print(new ArrayList<String>(), "SimpleString");

or

print(new ArrayList<Integer>(),5)

because <T> is a type and it cannot be both String and integer at same time.

Your call could work if you do something like this :

public static <T, K> void print(List<T> a, K b){
    System.out.println(a);
    System.out.println(b);
}

public static void main(String[] args){
    print(new ArrayList<String>(),1);
}
like image 22
Amal Avatar answered Sep 22 '22 03:09

Amal