4

In the following code snippet, there are three versions of a method named show().

package overloading;

import java.util.ArrayList;
import java.util.List;

public final class Main
{
    private void show(Object object)
    {
        System.out.println("Object");
    }

    private void show(List<Object> list)  //Unused method
    {
        System.out.println("List");
    }

    private void show(Object[] objects)
    {
        System.out.println("Objects");
    }

    private void addToList()
    {
        List<String>list=new ArrayList<String>();
        list.add("String1");
        list.add("String2");
        list.add("String3");
        show(list); // Invokes the first version

        String []s={"111", "222", "333"};
        show(s);   // Invokes the last version
    }

    public static void main(String[] args)
    {
        new Main().addToList();
    }
}

In this simplest of Java code, this method call show(s); (the last line in the addToList() method) invokes the last version of the overloaded methods. It supplies an array of strings - String[] and it is accepted by the receiving parameter of type Object[].

This function call show(list); however attempts to invoke the first version of the overloaded methods. It passes a list of type strings - List<String> which should be accepted by the middle version whose receiving parameter is of type List<Object> The middle version of the methods is completely unused. It is a compile-time error, if the first version is removed.

Why does this call show(list); not invoke this version - private void show(List<Object> list){} - the middle one?

Tiny
  • 27,221
  • 105
  • 339
  • 599
  • +1. Interesting. Apparently, the compiler takes the generic type annotation into consideration and eliminates the overloaded method it would otherwise have chosen. I would have expected a compile error because the generic types don't match instead. If you remove the type annotation from `list` (make it `List list`), then the middle method does get chosen (with warnings and runtime error of course). – Thilo Dec 26 '12 at 01:59
  • 1
    The middle version is chosen when the generic type parameter with `list` is removed. – Tiny Dec 26 '12 at 02:03
  • With autoboxing, varargs and generics, method dispatch has becoming really complex since Java 5. This makes for a great (or at least tricky) exam question. – Thilo Dec 26 '12 at 02:05
  • 1
    A good explanation of this is Brian Goetz's article [_Generic gotchas_](http://www.ibm.com/developerworks/java/library/j-jtp01255/index.html). – Ted Hopp Dec 26 '12 at 02:05

3 Answers3

7

In short, List<Object> is NOT List<String>.

To "fix" your code, use the following code

private void show(List<? extends Object> list)
{
    System.out.println("List");
}

Unlike arrays (which are covariant in Java), different instantiations of a generic type are not compatible to each other, not even explicitly.

With the declaration Generic<Supertype> superGeneric; Generic<Subtype> subGeneric; the compiler would report a conversion error for both castings (Generic<Subtype>)superGeneric and (Generic<Supertype>)subGeneric.

This incompatibility may be softened by the wildcard if ? is used as actual type parameter: Generic<?> is the abstract supertype for all instantiations of the generic type.

Also see

Tiny
  • 27,221
  • 105
  • 339
  • 599
Xiao Jia
  • 4,169
  • 2
  • 29
  • 47
4

List<Object> is not a superclass of List<String> in java. What you are assuming is that Java has covariance on generics, which it does NOT.

What this means is that if A is a superclass of B, List<A> is NOT a superclass of List<B>

A similar problem is faced in Cannot convert generic to expanded nested type, You can see if any of the work arounds there works for you.

Perhaps changing

private void show(List<Object> list)

to

private void show(List<? extends Object> list)

Would work as you would expect?

Community
  • 1
  • 1
Karthik T
  • 31,456
  • 5
  • 68
  • 87
  • @Thilo why would there be an error? There might have been an error if the `show(Object object)` version did not exist. – Karthik T Dec 26 '12 at 01:58
  • I would not have expected that `List` gets discarded as the best choice by the compiler. I would have expected it to still dispatch there (but then complain about the mismatch). That seems to be more predictable behaviour to me. I would not have expected generics to affect which methods gets chosen. But seems I was wrong. With autoboxing, varargs and generics, method dispatch has becoming *really* complex since Java 5. This makes for a great (or at least tricky) exam question. – Thilo Dec 26 '12 at 02:01
  • @Thilo That is the reason for the failure, It would have been picked if `list` was a `List`, But since it is an UNRELATED type to `List`, it fails. If java supported covariance like Scala does, it would have worked. – Karthik T Dec 26 '12 at 02:03
  • Yes, and since Java does not, I would have preferred a compile error to silently dispatching to the "wrong" method. – Thilo Dec 26 '12 at 02:04
  • @Thilo The problem is there is no inherent compile error here. You are assuming its an error since you expect that List is a superclass of List . The best you can expect in such cases is a compiler warning (in its most paranoid setting) or a feature in the [Lint type applications](http://en.wikipedia.org/wiki/Lint_(software)) to catch this – Karthik T Dec 26 '12 at 02:07
  • @Thilo - But it's not the wrong method. It's just not what you (er, OP) intended, but how is the compiler supposed to know that? It found a matching method and was happy. – Ted Hopp Dec 26 '12 at 02:07
  • In other words, I had thought that adding type annotations to your program may make it stop compiling because of additional checks, but does not affect *how* it gets compiled. – Thilo Dec 26 '12 at 02:07
  • @TedHopp: "It found a matching method and was happy." Yes, the problem is that those matching rules have to become too complex. It should not match another method because of the type annotation IMO. All this should do is reject the method otherwise matched and then *error out* (not choose another one). Principle of Least Surprise definitely violated here. – Thilo Dec 26 '12 at 02:10
  • @KarthikT: No. What I would have expected (and wanted) is that it does not consider generics at all when selecting the method. That would match `List` (with Object "erased"). But that match would then be discarded and compilation aborted because of the generic type mismatch. Since, as you have pointed out, there is no generic covariance and no generic overloading in Java anyway, why drag generics into method selection at all? – Thilo Dec 26 '12 at 02:17
  • @Thilo - But what if one wanted `show(Object object)` specifically to handle all arguments that were not `List` or `Object[]`? That seems like a perfectly reasonable thing to do and you would change the language to make it impossible! – Ted Hopp Dec 26 '12 at 03:56
  • @TedHopp: You can cast to Object to achieve that. Let me ask you then, what if one wanted to also have a `show(List)` or a `show(List>)` or a `show(List)` in addition? Java does not let you do that. There is no generic covariance and no generic overloading in Java anyway, so why drag generics into method selection at all? – Thilo Dec 26 '12 at 04:35
  • @Thilo - I agree; Java generics are very weak in that regard. Good point about requiring an explicit cast. It would certainly remove the mystery from how OP's code works (or doesn't, depending on your pov). – Ted Hopp Dec 26 '12 at 04:55
3

I would say it is because the parameters are different, List <Object> Differs from List <String> Therefore when you call the overloaded method, it would default to the first one accepting just an Object.

Here is a short example:

public class Test
{
  public static void overload (Object o)
  {
    System.out.println ("Object");

  }

   public static void overload (List <Object> o)
  {
    System.out.println ("List Object");
  }

   public static void main (String [] args)
   {
     overload (new ArrayList <Object>()); //"List Object"
     overload (new ArrayList <String>()); //"Object"

   }
}

Parameterize the list with generics and everything should work.

A--C
  • 36,351
  • 10
  • 106
  • 92