EMPTY_SET.iterator() and generics  
Author Message
Eric Sosman





PostPosted: 2007-2-23 3:38:00 Top

java-programmer, EMPTY_SET.iterator() and generics
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?

--
Eric Sosman
email***@***.com
 
Tor Iver Wilhelmsen





PostPosted: 2007-2-23 4:38:00 Top

java-programmer >> EMPTY_SET.iterator() and generics P氓 Thu, 22 Feb 2007 20:38:06 +0100, skrev Eric Sosman
<email***@***.com>:

> 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<?>
 
Chris Uppal





PostPosted: 2007-2-23 4:43:00 Top

java-programmer >> EMPTY_SET.iterator() and generics Eric Sosman wrote:

> 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


 
 
Eric Sosman





PostPosted: 2007-2-23 6:25:00 Top

java-programmer >> EMPTY_SET.iterator() and generics Chris Uppal wrote:
> Eric Sosman wrote:
>
>> 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.

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.

--
Eric Sosman
email***@***.com
 
 
Piotr Kobzda





PostPosted: 2007-2-23 6:36:00 Top

java-programmer >> EMPTY_SET.iterator() and generics Eric Sosman wrote:

> 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
 
 
Lew





PostPosted: 2007-2-23 6:59:00 Top

java-programmer >> EMPTY_SET.iterator() and generics Eric Sosman wrote:
>> Interestingly, the apparently equivalent
>>
>> Set<Inventory> set = (onhand == null)
>> ? Collections.emptySet() : onhand;
>> return set.iterator();
>>
>> did not work.

Piotr Kobzda wrote:
> 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
 
 
Eric Sosman





PostPosted: 2007-2-23 7:08:00 Top

java-programmer >> EMPTY_SET.iterator() and generics Lew wrote:
> [...]
> 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 ...

--
Eric Sosman
email***@***.com
 
 
Lew





PostPosted: 2007-2-23 7:26:00 Top

java-programmer >> EMPTY_SET.iterator() and generics > Lew wrote:
>> I am still feeling my way through the maze of generics, raw types and
>> type erasure.

Eric Sosman wrote:
> 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
 
 
Chris Uppal





PostPosted: 2007-2-23 22:48:00 Top

java-programmer >> EMPTY_SET.iterator() and generics
Lew wrote:

> Eric Sosman wrote:
> > > Interestingly, the apparently equivalent
> > >
> > > Set<Inventory> set = (onhand == null)
> > > ? Collections.emptySet() : onhand;
> > > return set.iterator();
> > >
> > > did not work.
>
> Piotr Kobzda wrote:
> > 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,

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


 
 
Lew





PostPosted: 2007-2-24 7:05:00 Top

java-programmer >> EMPTY_SET.iterator() and generics Lew wrote:
>> Maybe because the first example used a raw type on the left of the :, and
>> a parametrized type on the right,
>
Chris Uppal wrote:
> 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<Object>" is not.

> 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<Object>", not "Type<? extends Object>" (i.e., "Type<?>").

> 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
 
 
Piotr Kobzda





PostPosted: 2007-2-28 16:58:00 Top

java-programmer >> EMPTY_SET.iterator() and generics Lew wrote:
> Lew wrote:
>>> Maybe because the first example used a raw type on the left of the :,
>>> and
>>> a parametrized type on the right,
>>
> Chris Uppal wrote:
>> 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<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).

>
>> 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<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]


>> 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.

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
 
 
Chris Uppal





PostPosted: 2007-2-28 21:36:00 Top

java-programmer >> EMPTY_SET.iterator() and generics Piotr Kobzda wrote:

> 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


 
 
Lew





PostPosted: 2007-3-1 14:19:00 Top

java-programmer >> EMPTY_SET.iterator() and generics Chris Uppal wrote:
> (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