Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / March 2007

Tip: Looking for answers? Try searching our database.

EMPTY_SET.iterator() and generics

Thread view: 
Eric Sosman - 22 Feb 2007 19:38 GMT
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?

Signature

Eric Sosman
esosman@acm-dot-org.invalid

Tor Iver Wilhelmsen - 22 Feb 2007 20:37 GMT
På Thu, 22 Feb 2007 20:38:06 +0100, skrev Eric Sosman  
<esosman@acm-dot-org.invalid>:

>      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 - 22 Feb 2007 20:43 GMT
> 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 - 22 Feb 2007 22:25 GMT
>> return (onhand == null
>>     ? (Set<Inventory>)Collections.EMPTY_SET : onhand)
[quoted text clipped - 16 lines]
> 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.

Signature

Eric Sosman
esosman@acm-dot-org.invalid

Piotr Kobzda - 22 Feb 2007 22:35 GMT
>     This didn't work, but a close variant did:
>
[quoted text clipped - 12 lines]
>
> 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 - 22 Feb 2007 22:59 GMT
Eric Sosman wrote:
>>     Interestingly, the apparently equivalent
>>
[quoted text clipped - 3 lines]
>>
>> 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();

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 - 22 Feb 2007 23:07 GMT
> [...]
> 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 ...

Signature

Eric Sosman
esosman@acm-dot-org.invalid

Lew - 22 Feb 2007 23:26 GMT
>> 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 ...

Plugh!

- Lew
Chris Uppal - 23 Feb 2007 14:48 GMT
> Eric Sosman wrote:
> > >     Interestingly, the apparently equivalent
[quoted text clipped - 14 lines]
> 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 - 23 Feb 2007 23:04 GMT
Lew wrote:
>> 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 ?

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 - 28 Feb 2007 08:58 GMT
> Lew wrote:
>>> Maybe because the first example used a raw type on the left of the :,
[quoted text clipped - 6 lines]
> 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
[quoted text clipped - 5 lines]
> 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
[quoted text clipped - 5 lines]
>
> 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 - 28 Feb 2007 13:36 GMT
> 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 - 01 Mar 2007 06:18 GMT
> (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


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.