EMPTY_SET.iterator() and generics

Discussion in 'Java' started by Eric Sosman, Feb 22, 2007.

  1. Eric Sosman

    Eric Sosman Guest

    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
    lid
     
    Eric Sosman, Feb 22, 2007
    #1
    1. Advertising

  2. 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<?>
     
    Tor Iver Wilhelmsen, Feb 22, 2007
    #2
    1. Advertising

  3. Eric Sosman

    Chris Uppal Guest

    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
     
    Chris Uppal, Feb 22, 2007
    #3
  4. Eric Sosman

    Eric Sosman Guest

    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
    lid
     
    Eric Sosman, Feb 22, 2007
    #4
  5. Eric Sosman

    Piotr Kobzda Guest

    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
     
    Piotr Kobzda, Feb 22, 2007
    #5
  6. Eric Sosman

    Lew Guest

    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
     
    Lew, Feb 22, 2007
    #6
  7. Eric Sosman

    Eric Sosman Guest

    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
    lid
     
    Eric Sosman, Feb 22, 2007
    #7
  8. Eric Sosman

    Lew Guest

    > 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
     
    Lew, Feb 22, 2007
    #8
  9. Eric Sosman

    Chris Uppal Guest

    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
     
    Chris Uppal, Feb 23, 2007
    #9
  10. Eric Sosman

    Lew Guest

    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
     
    Lew, Feb 23, 2007
    #10
  11. Eric Sosman

    Piotr Kobzda Guest

    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
     
    Piotr Kobzda, Feb 28, 2007
    #11
  12. Eric Sosman

    Chris Uppal Guest

    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
     
    Chris Uppal, Feb 28, 2007
    #12
  13. Eric Sosman

    Lew Guest

    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
     
    Lew, Mar 1, 2007
    #13
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Juergen Berchtel
    Replies:
    1
    Views:
    6,010
    John C. Bollinger
    May 20, 2005
  2. Hendrik Maryns
    Replies:
    18
    Views:
    1,429
  3. Neroku

    About generics and Iterator

    Neroku, Feb 10, 2007, in forum: Java
    Replies:
    2
    Views:
    312
  4. Soul
    Replies:
    0
    Views:
    527
  5. Replies:
    4
    Views:
    186
    Christophe Grandsire
    Oct 28, 2005
Loading...

Share This Page