Generics Method Argument Behavior

Nizam :

Consider a generic method program below:

class GenericTest {
    int i;
}
public class GenericMethod {

    public <T> List<T> addList(T t, T t1) {
        List<T> list=new ArrayList<T>();
        list.add(t);
        list.add(t1);
        return list;
    }
    public <T> List<T> addList(T t, T t1, List<T> l) {
        List<T> list=new ArrayList<T>();
        list.add(t);
        list.add(t1);
        list.addAll(l);
        return list;
    }
    public static void main(String[] args) {
        GenericMethod gm=new GenericMethod();
        List l=gm.addList(new Integer(42), "java");           //Line 21
        System.out.println(l);
        List<Object> list = new ArrayList<Object>();
        list.add(false);
        List l2 = gm.addList("java", new Integer(42), list);     //Line 25
        System.out.println(l2);
        GenericTest gt = new GenericTest();
        List l3 = gm.addList("java", gt);          //Line 28
        System.out.println(l3);
        List l4 = gm.addList(gm, gt);              //Line 30
        System.out.println(l4);
        List lst = new ArrayList();
        lst.add(new Object());
        List l5 = gm.addList("java", new Integer(42), lst);    //Line 34
        System.out.println(l5);
        System.out.println(l5.get(0));
    }
}

By going through the Reference link Java Generics - Confusing behavior I am infering that,

In Line 21 since the method is called with both String and Integer, it calls the method with Object for T. Same goes for Line 25 as List has also type Object, it calls the method with Object for T. Calling List with other type will produce an error. Is my inference correct?

What I cannot thought is about

1) Line 28 when String and class is passed,

2) Line 30 when two different class is passed,

3) Line 34 when List declared without type is passed as argument.

Can anyone share their knowledge to clear my understanding. Any help would be appreciated! Thanks!

rzwitserloot :

Calling List with other type will produce an error. Is my inference correct?

Yes; you cannot pass a List<String> there, for example. Generally the intent of your second addList method is just wrong. The correct signature is this:

public <T> List<T> addList(T t1, T t2, List<? extends T> list)

note the ? extends syntax. If all you do is read from the list, there's no reason not to add that. A List of some specific subtype of T is guaranteed to contain Ts. The reason there's a difference at all: What if you wanted to ADD to this list? Let's say T is Number and you pass in a list of Integer. Double is a number. With List<T> list you could call list.add(5.0) and it would compile and run, putting doubles into your list of integers. With a List<? extends T>, the add call is always a compiler error (unless you try to add null which would work). For reading, you get a T out, whether you're calling get on a List<? extends T> or a List<T>, it does not matter.

1) Line 28 when String and class is passed,

No, you're passing an instance of type String and an instance of type GenericTest. String is just as much a class as GenericTest is. String is not special. This is exactly the same scenario as your call in line 21: You're passing 2 expressions; one of object type X and the other of object type Y. Therefore, T is inferenced to be the most specific shared type (possibly, a so-called lub type: A set of types) between the 2 passed instances. In the case of line 21, Integer and String most specific shared type is simply Object (to be technical, actually its the lub type Object & Serializable, but that mostly doesn't matter here). In line 28 it's the most specific shared type between String and GenericTest which is still Object, no difference.

2) Line 30 when two different class is passed,

See above; precisely the same situation. The most specific shared type between GenericTest and GenericMethod is Object.

3) Line 34 when List declared without type is passed as argument.

The expression lst in line 34 is of 'raw' type ArrayList. when you use raw types, two things happen: [1] the compiler will warn you that you're using raw types, and [2] pretty much all generics tests and checks are disabled for any calls that involve any raw types, so, the compiler will just let this happen.

Remember, generics are a figment of the compiler's imagination. Their point is for the compiler to tell you that your code is broken. That's all.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=82636&siteId=1