5

Say, I have a method:

public static <T> Collection<T> addToCollection(T element, Collection<T> collection) {
    collection.add(element);
    return collection;
}

And then when trying to compile this code:

Integer i = 42;
Collection<Integer> result = addToCollection(i, Collections.emptyList());

I get an error Type mismatch: cannot convert from Collection<Object> to Collection<Integer>. Could anyone explain why the type system cannot infer that Collections.emptyList() should be of type Collection<Integer>?

The example above is obviously quite artificial, but I stumble upon that limitation all the time and it's really annoying. After having read Effective Java I have found out that you can simply do Collections.<Integer>emptyList() (must say, that was quite a revelation for me at the time) and have everything compiling smoothly, but when you have some complicated type then it really is a nuisance.

I'm just wondering if this is some sort of bug, or are there any valid reasons for it to work that way?

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
siledh
  • 3,268
  • 2
  • 16
  • 29
  • 9
    Could you kindly rephrase the title? – Kobi Jan 21 '14 at 12:30
  • 3
    Talk about pot calling the kettle black. Collections.emptyList() returns an unmodifiable List, so the code wouldn't work anyways. – Kayaman Jan 21 '14 at 12:32
  • 3
    @Kayaman beside the point, as OP indicated himself. The question is about the principles of inference, runtime does not figure into it. – Marko Topolnik Jan 21 '14 at 12:33
  • 2
    @MarkoTopolnik I know what the question is about, stop patronizing me. I'm pointing out that he wrote a "retarded" example while complaining about the "retarded" type inference. – Kayaman Jan 21 '14 at 12:35
  • 2
    @Kayaman You are patronizing OP, which makes you fair game. – Marko Topolnik Jan 21 '14 at 12:36
  • It perfectly makes sense because java check types :) and that is why generic is for. Maybe would be better if you explainuse what you would like to implement instead of using stupid title. (and read somethink about java and Generic) – JosefN Jan 21 '14 at 12:38
  • Sorry for the title. It's just that English is not my native language and I didn't find the word *retarded* that offensive myself (apparently it *is* much more than I thought). Also, I was mildly irritated at the time of writing. – siledh Jan 21 '14 at 12:44

3 Answers3

11

The type inference system has been improved in Java 8, with the introduction of target typing, in order to give more expressivity to streams and lambdas. As a consequence your code compiles with Java 8.

More about it in the updated tutorial, with a very similar example towards the very bottom of the page.

assylias
  • 321,522
  • 82
  • 660
  • 783
  • TIL! That's awesome -- I've muttered ugly words at javac myself, in these situations. I'll have to try this out tomorrow (don't have jdk 8 on this machine). Will I be disappointed if I also expect this better inference to work with ternaries? `List foo = myBool ? someList : Collections.emptyList()` – yshavit Jan 21 '14 at 12:37
  • 1
    @yshavit That compiles too. – assylias Jan 21 '14 at 12:49
  • 1
    Many years ago, at the time of the epic BGGA/CICE/FCM fight, I read [somewhere](https://weblogs.java.net/blog/carcassi/archive/2010/04/09/two-problems-generics-java-0) that "Java should focus on fixing its Generics instead of bringing in yet another layer of complexity". Turns out they did the former as a service to the latter :) – Marko Topolnik Jan 21 '14 at 12:54
0

Collections.emptyList() returns a List<Object> which is passed through your addToCollection() method. And since Object is not Integer, this fails.

rec
  • 10,340
  • 3
  • 29
  • 43
  • This is not true. That's a generic method. – Rohit Jain Jan 21 '14 at 12:34
  • ` public static List emptyList()`---introduced as a method wrapper around the constant `EMPTY_LIST` just for this one reason. – Marko Topolnik Jan 21 '14 at 12:36
  • It is a generic method, but there are two options where to bind the generic type: either based on the arguments or based on the return type. Here, obviously the binding happens based on the second argument that you pass in. – rec Jan 21 '14 at 12:36
  • Java (at least up to 7 as we note in the reply below) can only correctly infer the return type of a method if it is assigned to a variable. E.g. List x = Collections.emptyList() works, but if you pass Collections.emptyList() into some method as a parameter, there is no inference. It defaults back to Object. – rec Jan 21 '14 at 12:40
0

Whats harm in doing this?

Integer i = 42;
List<Integer> emptyList = Collections.emptyList();
Collection<Integer> result = addToCollection(i, emptyList);

In java 8 it will be taken care.

chaosguru
  • 1,933
  • 4
  • 30
  • 44