• Post Reply Bookmark Topic Watch Topic
  • New Topic

Stream .collect() Error: incompatible types

 
Tim Cooke
Sheriff
Posts: 3294
153
Clojure IntelliJ IDE Java
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Here's a little quirk (i.e. thing I don't understand) about Streams. This code:

Produces this error at runtime:

If I replace the Collections.EMPTY_SET with Collections.emptySet() then there is no runtime error.

It looks like the object arriving at the .collect() operation is of type Object instead of CustomType which I assume is down to type erasure but I don't understand why. The unparameterised type, the Set, is a constructor param and I don't get how it affects the construction of the CustomType?
 
Campbell Ritchie
Marshal
Posts: 52574
119
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Let's have a look. Collections#EMPTY_SET simply says
The empty set (immutable). This set is serializable.
Whereas the Collections#emptySet() method seems to have types; it is shown as a parameterised method returning a Set<T>. It also says
Unlike this method, the field does not provide type safety.
So the method provides an empty Set of a particular type (the link shows an example for a Set<String> and the field doesn't seem to have a type. Maybe it is a Set<?> rather than a Set<T>. If Java® had used reification, we could have used
EMPTY_SET.getClass().getActualParameters()
but this sort of method only gives you the formal type parameters. Trying EMPTY_SET first and the method after, I got this:
java TypeDemo
[E]
[E]
and the toGenericString method didn't tell us much more:-
java TypeDemo
private static class java.util.Collections$EmptySet<E>
private static class java.util.Collections$EmptySet<E>
I have tried and failed to replicate your problem with some non‑Google classes.Changing around between emptySet and EMPTY_SET made no difference, but I had some warnings about type‑safety.
 
Tim Cooke
Sheriff
Posts: 3294
153
Clojure IntelliJ IDE Java
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think I could understand it if CustomType was parameterised, but it's not, it's just a normal POJO. It's only the constructor arg that's a parameterised Set. Here's the equivalent example using just the standard lib so you can try it out:
 
Stephan van Hulst
Bartender
Posts: 6583
84
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The problem is that thing -> new CustomType(Collections.EMPTY_SET) is inferred to be a raw Function, instead of a Function<CustomType, CustomType>.

I've spent some time going through the JLS, but the relevant topics are so dense, I can't tell whether this is designed or a bug. It has to do with a very arcane combination of invocation type inference and the type of a lambda expression.

Somehow the fact that an unchecked conversion takes place records the custom object as a raw type, because this doesn't work either:
But this does:
Since type inference should only work using the formal types involved, and the relevant JLS topics don't really mention raw types, I suspect that this is a bug or an oversight in the compiler.

Of course, you can avoid all this mess by not using raw types in the first place. It's very puzzling though.
 
Campbell Ritchie
Marshal
Posts: 52574
119
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Your code runs all right, but I get unchecked type‑safety warnings but the warnings go away if I use emptySet() throughout instead of EMPTY_SET.
 
Stephan van Hulst
Bartender
Posts: 6583
84
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
What javac version are you using Campbell? I'm on 1.8.0_74.
 
Tim Cooke
Sheriff
Posts: 3294
153
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Curious it runs for you Campbell.

I, like Stephan, am on javac 1.8.0_74
 
Campbell Ritchie
Marshal
Posts: 52574
119
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
i am on 8u77/Ubuntu14.04LTS and i am having difficulty downloading the older versions. I found a download for 8u66
java -versionjava version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)
campbell@campbellsComputer:xyz$ javac -version
javac 1.8.0_66
… and got some unchecked warnings
javac EmptySetDemo.java
Note: EmptySetDemo.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
campbell@campbellsComputer:xyz$ java EmptySetDemo
[EmptySetDemo$SetContainer@776ec8df]
javac -Xlint:unchecked EmptySetDemo.java
EmptySetDemo.java:14: warning: [unchecked] unchecked method invocation: constructor <init> in class EmptySetDemo.SetContainer is applied to given types
list.add(new SetContainer<>(EMPTY_SET));
^
required: Set<T>
found: Set
where T is a type-variable:
T extends Object declared in class EmptySetDemo.SetContainer
EmptySetDemo.java:14: warning: [unchecked] unchecked conversion
list.add(new SetContainer<>(EMPTY_SET));
^
required: Set<T>
found: Set
where T is a type-variable:
T extends Object declared in class EmptySetDemo.SetContainer
EmptySetDemo.java:14: warning: [unchecked] unchecked method invocation: method add in interface List is applied to given types
list.add(new SetContainer<>(EMPTY_SET));
^
required: E
found: EmptySetDemo.SetContainer
where E is a type-variable:
E extends Object declared in interface List
3 warnings
[1]+ Done pluma EmptySetDemo.java
So it does seem to have to do with EMPTY_SET being not type‑safe. The warning goes away if I change it to emptySet().
But I had no difficulty persuading it to run on 8u66. I hope that isn't a platform‑dependent feature
 
Stephan van Hulst
Bartender
Posts: 6583
84
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Then I'm going to assume this is a bug.
 
Campbell Ritchie
Marshal
Posts: 52574
119
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Or at least an oversight. It becomes obvious from the -Xlint printout that EMPTY_SET is a raw type and emptySet() doesn't return a raw type. You are right, Stefan, to report that. Or maybe Tim should since he found the problem in the first place.
 
Winston Gutkowski
Bartender
Posts: 10571
64
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:Or at least an oversight. It becomes obvious from the -Xlint printout that EMPTY_SET is a raw type

It's actually obvious from the docs, which also suggest that it's been around since 1.2.

Further evidence (if any were needed) that it's almost always WRONG to expose variables directly.
If emptySet() had been available from the outset, and EMPTY_SET kept private, you wouldn't have any of these problems.

Oddly enough, I used precisely that set-up for an emptyIterator(), so I'm feeling rather pleased with myself.

Winston
 
Campbell Ritchie
Marshal
Posts: 52574
119
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yes, the docs give no “since” for EMPTY_SET, suggesting it was introduced with the rest of the class in 1.2, and emptySet() was introduced in 1.5 and returns Set<T>.
I still think it's an oversight, that they updated most of the class when generics was introduced in Java5 and forgot the three empty fields. The other two have types Map and List.

Do you remember they promised to prohibit raw types after Java6?
 
Rob Spoor
Sheriff
Posts: 20820
68
Chrome Eclipse IDE Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:Yes, the docs give no “since” for EMPTY_SET, suggesting it was introduced with the rest of the class in 1.2, and emptySet() was introduced in 1.5 and returns Set<T>.
I still think it's an oversight, that they updated most of the class when generics was introduced in Java5 and forgot the three empty fields. The other two have types Map and List.

Do you remember they promised to prohibit raw types after Java6?

They didn't forget - they couldn't make any changes or a lot of code would be broken.

Think about it. What would the type be? Set<Object>? Then anybody who uses it for Set<String> or Set<Integer> has code that won't compile.
 
Campbell Ritchie
Marshal
Posts: 52574
119
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You can't make it Set<T>?

We are back to the bit about the method providing type safety. Maybe they should have added that comments to the EMPTY_SET documentation pointing out that the field is not type safe.
 
Rob Spoor
Sheriff
Posts: 20820
68
Chrome Eclipse IDE Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
A public static final field has no generic type <T> that can be inferred from anywhere, so no, Set<T> is not possible.

I think they should have gone as far as to deprecate the fields. They're still used internally by the methods, but those provide type safety. Everybody should just switch to the methods, and deprecating the fields is one way to indicate that this should happen ASAP.
 
Campbell Ritchie
Marshal
Posts: 52574
119
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Or at least a bit more of a warning with the fields rather than only with the methods. Saying EMPTY_SET is for compatibility with legacy code would be very helpful.
 
phool sinha
Greenhorn
Posts: 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
thanks for this it"s very useful for me
 
Campbell Ritchie
Marshal
Posts: 52574
119
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Welcome to the Ranch
 
Winston Gutkowski
Bartender
Posts: 10571
64
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
phool sinha wrote:thanks for this it"s very useful for me

You're most welcome.

Nice to know that our philosophical meanderings have some use.

Winston
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!