test
test

Reputation: 81

Why generic version of Collection parameter is getting call

Here is one code segment

public class HelloWorld{

    public static void main(String[] args) { 
        GenericClass<Integer> test = new GenericClass<Integer>(); 
        test.method(new ArrayList<Integer> ()); 
    } 
}
class GenericClass<T> {
    public void overloadedMethod( Collection<?> o) { 
        System.out.println("overloadedMethod(Collection<?>)"); 
    } 
    public void overloadedMethod( List<Number> s) { 
        System.out.println("overloadedMethod(List<Number>)"); 
    } 
    public void overloadedMethod( ArrayList<Integer> i) { 
        System.out.println("overloadedMethod(ArrayList<Integer>)"); 
    }

    public void method(List<T> t) { 
        overloadedMethod(t) ; // which method is called? 
    }   
}

I am expecting it would call overloaded method with Arraylist parameter but Collection is getting called why?

Upvotes: 8

Views: 82

Answers (2)

dkatzel
dkatzel

Reputation: 31648

This exact code is used in an example in Angelika Langer's webpage on java generics

I'll post the explanation in case the page ever goes down:

The program prints:

overloadedMethod(Collection)

One might have expected that version for ArrayList would be invoked, but that again is the wrong expectation. Let us see what the compiler translates the generic class to.

Example (after type erasure):

public final class GenericClass {

    private void overloadedMethod( Collection o) {
        System.out.println("overloadedMethod(Collection<?>)");
    }
    private void overloadedMethod( List s) {
        System.out.println("overloadedMethod(List<Number>)");
    }
    private void overloadedMethod( ArrayList i) {
        System.out.println("overloadedMethod(ArrayList<Integer>)");
    }

    private void method(List t) {
        overloadedMethod(t) ;
    }

    public static void main(String[] args) {
        GenericClass test = new GenericClass();
        test.method(new ArrayList ());
    }
} 

One might mistakenly believe that the compiler would decide that the List version of the overloaded method is the best match. But that would be wrong, of course. The List version of the overloaded method was originally a version that takes a List as an argument, but on invocation a List is passed, where T can be any type and need not be a Number . Since T can be any type the only viable version of the overloaded method is the version for Collection .

In fact, if you comment out the Collection method you will get a compiler error because the List and ArrayList are too specific to fit for all the possible <T> types.

Upvotes: 5

Ori Lentz
Ori Lentz

Reputation: 3688

Take a look at section 15.12 at the Java documention titled "Method Invocation Expressions". More specifically section 15.12.2.5 Choosing the most specific method:

If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the runtime method dispatch. The Java programming language uses the rule that the most specific method is chosen.

The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time error. In cases such as an explicitly typed lambda expression argument (§15.27.1) or a variable arity invocation (§15.12.2.4), some flexibility is allowed to adapt one signature to the other.

Moreover, section 8.4.9 called Overloadding states:

When a method is invoked (§15.12), the number of actual arguments (and any explicit type arguments) and the compile-time types of the arguments are used, at compile time, to determine the signature of the method that will be invoked (§15.12.2). If the method that is to be invoked is an instance method, the actual method to be invoked will be determined at run time, using dynamic method lookup (§15.12.4).

Taking those two sections into account, we can determine that since method is passed a List<T> object, the most specific target Java finds is that of a wildcard Collections.

Note that even if you had changed the argument to ArrayList<T> as such:

public void method(ArrayList<T> t) { 
    overloadedMethod(t) ; // which method is called?
} 

The same method will still be called, because the specific overload that accepts ArrayList is too specific - ArrayList<Integer> vs. your argument that is ArrayList<T> and thus Java determines Collection<?> to be the best match (you can of course circumvent it by either changing the parameter type of method or making the parameter of overloadedMethod more specific).

Upvotes: 4

Related Questions