EMPTY_SET.iterator() and generics

E

Eric Sosman

In the old days, before generics, I wrote

private SortedSet onhand;

Iterator getInventoryIterator() {
return (onhand == null ? Collections.EMPTY_SET ? onhand)
.iterator();
}

.... the idea being to create the SortedSet only if there are
actually some Inventory objects to store in it, and to return
a suitable Iterator whether or not the SortedSet exists.

Now, in an effort to leave prehistory behind me and adopt
generics with all their benefits, I change the SortedSet to a
SortedSet<Inventory> and change the return type of the method
to Iterator<Inventory> -- but how do I rewrite the guts of the
method to get the compiler to understand that all is well? The
problem seems to be that Collections.EMPTY_SET.iterator() returns
an Iterator<Object>, and the compiler complains when I try to
return this in place of the desired Iterator<Inventory>. I've
tried a few variations, but have only succeeded in moving the
warning message around, not in eliminating it:

return (onhand == null
? (Set<Inventory>)Collections.EMPTY_SET : onhand)
.iterator();

return (Iterator<Inventory>)
(onhand == null ? Collections.EMPTY_SET ? onhand)
.iterator();

This seems like a lot of trouble to take over the empty set,
but there ought to be *some* clean way to do it. Ideas?
 
T

Tor Iver Wilhelmsen

PÃ¥ Thu, 22 Feb 2007 20:38:06 +0100, skrev Eric Sosman
This seems like a lot of trouble to take over the empty set,
but there ought to be *some* clean way to do it. Ideas?

Check the methods Collections.emptySet() etc. which return "genericified"
Set<?>
 
C

Chris Uppal

Eric said:
return (onhand == null
? (Set<Inventory>)Collections.EMPTY_SET : onhand)
.iterator();

Wouldn't something like

if (onhand == null)
return Collections.emptySet().iterator();
else
return onhand.iterator();

do the trick ?

I don't know whether you'd be allowed to reduce that to a ternary conditional
expression. And I'm not even going to guess -- I haven't had a lot of luck
using ?: with the type inference stuff, and still less in understanding under
what circumstances it is supposed to work. Same goes for the circumstances
under which it is necessary to assign to an intermediate variable rather than
trusting that javac will infer the desired type from the return type of the
method it's called from.

-- chris
 
E

Eric Sosman

Chris said:
Wouldn't something like

if (onhand == null)
return Collections.emptySet().iterator();
else
return onhand.iterator();

do the trick ?

I don't know whether you'd be allowed to reduce that to a ternary conditional
expression. And I'm not even going to guess -- I haven't had a lot of luck
using ?: with the type inference stuff, and still less in understanding under
what circumstances it is supposed to work. Same goes for the circumstances
under which it is necessary to assign to an intermediate variable rather than
trusting that javac will infer the desired type from the return type of the
method it's called from.

This didn't work, but a close variant did:

Set<Inventory> set;
if (onhand == null)
set = Collections.emptySet();
else
set = onhand;
return set.iterator();

Interestingly, the apparently equivalent

Set<Inventory> set = (onhand == null)
? Collections.emptySet() : onhand;
return set.iterator();

did not work.

My thanks to you and to Tor Iver Wilhelmsen.
 
P

Piotr Kobzda

Eric said:
This didn't work, but a close variant did:

Set<Inventory> set;
if (onhand == null)
set = Collections.emptySet();
else
set = onhand;
return set.iterator();

Interestingly, the apparently equivalent

Set<Inventory> set = (onhand == null)
? Collections.emptySet() : onhand;
return set.iterator();

did not work.

I don't really know why. But similar, without any type inference in
effect, did:

return (onhand == null
? Collections.<Inventory> emptySet() : onhand)
.iterator();


piotr
 
L

Lew

Piotr said:
I don't really know why. But similar, without any type inference in
effect, did:

return (onhand == null
? Collections.<Inventory> emptySet() : onhand)
.iterator();

Maybe because the first example used a raw type on the left of the :, and a
parametrized type on the right, and one is not a subtype of the other. This
violates the compile-time checks on the ternary operator.

The second example used parametrized types on both sides of the :, allowing
compilation.

I am still feeling my way through the maze of generics, raw types and type
erasure.

- Lew
 
E

Eric Sosman

Lew said:
[...]
I am still feeling my way through the maze of generics, raw types and
type erasure.

The worst feature of the maze is the little dwarf who
suddenly appears, throws an axe at you, and then explains
why the compiler is right and you are wrong, taking twenty
minutes for the explanation ...
 
L

Lew

Eric said:
The worst feature of the maze is the little dwarf who
suddenly appears, throws an axe at you, and then explains
why the compiler is right and you are wrong, taking twenty
minutes for the explanation ...

Plugh!

- Lew
 
C

Chris Uppal

Lew said:
Maybe because the first example used a raw type on the left of the :, and
a parametrized type on the right,

Not sure what you mean by that ?

One branch is explicitly typed since onhand has type SortedSet<Inventory>. Why
should the inferencer come up with something different for
Collections.emptySet() ?

Is it possible that it could produce
SortedSet<? extends Inventory>
or something like that ? I don't see how/why myself, but then I've pretty much
given up on working out what the generics stuff does in tricky situations (let
alone what it /should/ do -- which I suspect is often different).

-- chris
 
L

Lew

Chris said:
Not sure what you mean by that ?

I take the terminology straight from the Java universe. A raw type is the type
after erasure, when used without a generic parameter. So "List" is a raw type,
"List said:
One branch is explicitly typed since onhand has type SortedSet<Inventory>. Why
should the inferencer come up with something different for
Collections.emptySet() ?

I am not saying that this is the One Right Way, but the reason is that the
Javba language treats raw types at compile time as somewhat equivalent to
Type said:
Is it possible that it could produce
SortedSet<? extends Inventory>
or something like that ?

Before Sun chose a different approach to raw types it might have been
possible, but Sun did not choose that approach. Furthermore, the style of Java
is mostly about explicit declarations; it does not do a whole lot of type
inference. The inclusion of generics is evidence that Sun favors an explicit
type system over an implicit one.
I don't see how/why myself, but then I've pretty much given up on working out what the generics stuff does in tricky situations (let
alone what it /should/ do -- which I suspect is often different).

When you check the rules for the ternary operator, it says that of the types
on both sides of the ':', one must be assignable (upcastable) to the other.

Set<Inventory> and Set<Object> do not fulfill that criterion.

Ergo, Set<Inventory> and the raw type Set do not, either.

Ergo, the ternary expression with a left side of the former type and right
side of the latter does not compile.

When the raw type was changed to one with the parameter <Inventory>, it made
both sides have the same non-raw type, ergo, it compiled.

- Lew
 
P

Piotr Kobzda

Lew said:
I take the terminology straight from the Java universe. A raw type is
the type after erasure, when used without a generic parameter. So "List"
is a raw type, "List<Object>" is not.

True. Beside of lack of raw types in "first" example...

In that example type argument inferred for Collections.emptySet() is:
<capture-of ? extends Object> (that's what the compiler is saying in
this case), which implies (regardless of what "capture-of ? extends
Object" is exact equivalent to) that a method's result type is _not a
raw type_.

In general, when the result type of a generic method (which is not
non-static member of erased type) refers to one (or more) of that
method's type variables, that result type is parameterized unless
unchecked conversion was necessary for the method to be applicable. For
such a methods the compiler always infers some type argument according
to the rules described in JLS3 (sections 15.12.2.7 and 15.12.2.8).
I am not saying that this is the One Right Way, but the reason is that
the Javba language treats raw types at compile time as somewhat
equivalent to "Type<Object>", not "Type<? extends Object>" (i.e.,
"Type<?>").

Java treats a raw types more like "Type<?>" (equivalent to "Type<?
extends Object>") than "Type<Object>", but in fact, it's none of them.

"Raw types are closly related to wildcards. Both are based on
existential types. Raw types can be thought of as wildcards whose type
rules are deliberately unsound, to accommodate interaction with legacy
code."
[from 'Discussion' in JLS3 4.8]

When you check the rules for the ternary operator, it says that of the
types on both sides of the ':', one must be assignable (upcastable) to
the other.

Set<Inventory> and Set<Object> do not fulfill that criterion.

Set<Inventory> and Set<?> do. Which is enough to successfully compile that:

Iterator<? extends Object> getInventoryIterator() {
return (onhand == null ? Collections.emptySet() : onhand)
.iterator();
}


However, the matter here is to guess why the compiler is unable to infer
more specific type argument for ternary conditional expression operand,
when it knows /very well/ both, an expected expression result type and
type of second operand?

Unfortunately, I still can't infer satisfactory answer from thick of JLS
rules. (It appears to me currently that the answer is somewhere around
chapter 15, with all that clear inference rules together with capture
conversion etc., but I can't give precise explanation and I'm close to
give up... :) ).


piotr
 
C

Chris Uppal

Piotr said:
Unfortunately, I still can't infer satisfactory answer from thick of JLS
rules. (It appears to me currently that the answer is somewhere around
chapter 15, with all that clear inference rules together with capture
conversion etc., but I can't give precise explanation and I'm close to
give up... :) ).

I have just taken another look at sections 15.12.2.7, and I /have/ given up !

(Did get one thing out of the exercise, though. Somehow I had been under the
impression that the type inference thing only applied to static generic
methods. I don't know where I got the idea from, but it's wrong. So that's
one less thing that I don't know anout generics ;-)

-- chris
 
L

Lew

Chris said:
(Did get one thing out of the exercise, though. Somehow I had been under the
impression that the type inference thing only applied to static generic
methods. I don't know where I got the idea from, but it's wrong. So that's
one less thing that I don't know anout generics ;-)

That's OK, I got the "raw type equals (roughly) capture-of-Object" thing wrong.

Generics in Java keep me humble. (Not really, but it's not their fault.)

-- Lew
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,135
Latest member
VeronaShap
Top