Type erasure bug?

Discussion in 'Java' started by Safalra, Aug 28, 2005.

  1. Safalra

    Safalra Guest

    Consider the following code:

    Vector<String> s=new Vector<String>(10);
    Vector o=s;
    o.add(new Object());
    Object p=s.get(0);

    Obviously this gives an unchecked warning about the second line.
    Because of type erasure the third line doesn't cause a run-time
    exception either. What surprises me is that the fourth line doesn't
    cause a class cast exception, despite accessing the Vector through s. I
    presume internally type erasure turns it into:

    Object p=(Object)(String)s.get(0);

    Which the compiler simplifies to:

    Object p=s.get(0);

    Is my understanding of this correct, and do people here regard this a
    bug in the compiler, or just another consequence of allowing unchecked
    exceptions in your code?

    (I discovered this while writing an article for a friend of mine who
    isn't a fan of Java - you can read it at
    http://www.safalra.com/programming/java/wrongerasure/ but remember that
    I *do* actually like Java in general, I just dislike certain features.)

    --
    Safalra (Stephen Morley)
    http://www.safalra.com/programming/
     
    Safalra, Aug 28, 2005
    #1
    1. Advertising

  2. Safalra wrote:
    > Consider the following code:
    >
    > Vector<String> s=new Vector<String>(10);
    > Vector o=s;
    > o.add(new Object());
    > Object p=s.get(0);
    >
    > Obviously this gives an unchecked warning about the second line.
    > Because of type erasure the third line doesn't cause a run-time
    > exception either. What surprises me is that the fourth line doesn't
    > cause a class cast exception, despite accessing the Vector through s. I
    > presume internally type erasure turns it into:
    >
    > Object p=(Object)(String)s.get(0);
    >
    > Which the compiler simplifies to:
    >
    > Object p=s.get(0);
    >
    > Is my understanding of this correct, and do people here regard this a
    > bug in the compiler, or just another consequence of allowing unchecked
    > exceptions in your code?


    If you try to subvert generic types on your own head be it. It seems
    reasonable to avoid unnecessary casts. It would be even more expensive
    to check super bounds.

    Beta versions of 5.0 had a bug where the removal of unnecessary casts
    went a little too far. If you had List<char[]> and then passed the
    elements to System.out.println, it would use System.out.println(Object)
    instead of System.out.println(char[]).

    Tom Hawtin
    --
    Unemployed English Java programmer
    http://jroller.com/page/tackline/
     
    Thomas Hawtin, Aug 28, 2005
    #2
    1. Advertising

  3. Safalra wrote:
    > Consider the following code:
    >
    > Vector<String> s=new Vector<String>(10);
    > Vector o=s;
    > o.add(new Object());
    > Object p=s.get(0);
    >
    > Obviously this gives an unchecked warning about the second line.
    > Because of type erasure the third line doesn't cause a run-time
    > exception either. What surprises me is that the fourth line doesn't
    > cause a class cast exception, despite accessing the Vector through s. I
    > presume internally type erasure turns it into:
    >


    Why would assigning anything (except a primitive type, which would cause
    a compiler error) to Object ever cause a class cast exception?

    Ray

    --
    XML is the programmer's duct tape.
     
    Raymond DeCampo, Aug 29, 2005
    #3
  4. Raymond DeCampo coughed up:
    > Safalra wrote:
    >> Consider the following code:
    >>
    >> Vector<String> s=new Vector<String>(10);
    >> Vector o=s;
    >> o.add(new Object());
    >> Object p=s.get(0);
    >>
    >> Obviously this gives an unchecked warning about the second line.
    >> Because of type erasure the third line doesn't cause a run-time
    >> exception either. What surprises me is that the fourth line doesn't
    >> cause a class cast exception, despite accessing the Vector through
    >> s. I presume internally type erasure turns it into:
    >>

    >
    > Why would assigning anything (except a primitive type, which would
    > cause a compiler error) to Object ever cause a class cast exception?



    You're missing the OP's point here.

    Generics allows you to omit the cast when retrieving values. Given the OP's
    vector of strings "s":

    String str = s.get(0);

    is allowed without requiring the

    String str = (String)s.get(0);

    cast that you would have to do pre-generics. That cast is "added in". So
    the question is what happens in the 4th line of his example:

    Object p = s.get(0);

    One would think that this might internally become:

    Object p = (String)s.get(0);

    because of "s" being a generic (Vector of String). That is where the CCE
    might have come in. What he has at slot 0 in the vector is a plain old
    Object, not a String. So his question is why doesn't this fail.


    --
    "Well, ain't this place a geographical oddity!
    Two weeks from everywhere!"
     
    Thomas G. Marshall, Aug 29, 2005
    #4
  5. "Thomas G. Marshall" <>
    wrote in message news:lmuQe.6198$fP.1084@trndny08...
    > Raymond DeCampo coughed up:
    >> Safalra wrote:
    >>> Consider the following code:
    >>>
    >>> Vector<String> s=new Vector<String>(10);
    >>> Vector o=s;
    >>> o.add(new Object());
    >>> Object p=s.get(0);
    >>>
    >>> Obviously this gives an unchecked warning about the second line.
    >>> Because of type erasure the third line doesn't cause a run-time
    >>> exception either. What surprises me is that the fourth line doesn't
    >>> cause a class cast exception, despite accessing the Vector through
    >>> s. I presume internally type erasure turns it into:
    >>>

    >>
    >> Why would assigning anything (except a primitive type, which would
    >> cause a compiler error) to Object ever cause a class cast exception?

    >
    >
    > You're missing the OP's point here.
    >
    > Generics allows you to omit the cast when retrieving values. Given the
    > OP's vector of strings "s":
    >
    > String str = s.get(0);
    >
    > is allowed without requiring the
    >
    > String str = (String)s.get(0);
    >
    > cast that you would have to do pre-generics. That cast is "added in". So
    > the question is what happens in the 4th line of his example:
    >
    > Object p = s.get(0);
    >
    > One would think that this might internally become:
    >
    > Object p = (String)s.get(0);
    >
    > because of "s" being a generic (Vector of String). That is where the CCE
    > might have come in. What he has at slot 0 in the vector is a plain old
    > Object, not a String. So his question is why doesn't this fail.



    Mine too. Surely if I wrote

    methodX(s.get(0))

    and methodX had overloads methodX(String) and methodX(Object), this should
    generate

    methodX((String)s.get(0))

    On the other hand, if methodX only has the overload methodX(Object), the
    cast in

    methodX((String)s.get(0))

    is unnecessary and, it can be argued, should be omitted. Is it the case,
    then, that the return type implied by the use of the generic method (i.e.
    "get()") determines whether the cast is applied? That seems evil to me,
    being the moral equivalent of overloading methods by return type. (And it
    would cause an odd bug if the overload methodX(String) were added after the
    caller was compiled) But I can't in all honesty say that I've thought this
    through.
     
    Mike Schilling, Aug 29, 2005
    #5
  6. Safalra

    Chris Uppal Guest

    Mike Schilling wrote:

    > Is it the case,
    > then, that the return type implied by the use of the generic method (i.e.
    > "get()") determines whether the cast is applied? That seems evil to me,
    > being the moral equivalent of overloading methods by return type. (And
    > it would cause an odd bug if the overload methodX(String) were added
    > after the caller was compiled)


    I'm not sure if I'm understanding what you consider "evil", but -- although
    surprising -- it doesn't seem all that damaging to me. After all the situation
    only arises when you mix generic and non-generic code in the same application
    /and/ expect the compiler to produce static type safety. Runtime type safety
    is still (I think) assured, since the compiler only omits "unnecessary"
    casts -- so runtime stuff that /should/ work /does/ work. The only thing
    that's omitted is that runtime code that (according to static tests) cannot be
    validated is allowed to run (or fail) dynamically, rather than duplicating the
    "fail even if it is not actually doing anything illegal" behaviour that the
    static type checking enforces.

    BTW, don't forget that the signature of the called method is part of the
    invokexxx bytecode(s), so if any other methodX(String) were added later, then
    that would be "invisible" to the code that was compiled before it existed.

    -- chris
     
    Chris Uppal, Aug 29, 2005
    #6
  7. Thomas G. Marshall wrote:
    > Raymond DeCampo coughed up:
    >
    >>Safalra wrote:
    >>
    >>>Consider the following code:
    >>>
    >>>Vector<String> s=new Vector<String>(10);
    >>>Vector o=s;
    >>>o.add(new Object());
    >>>Object p=s.get(0);
    >>>
    >>>Obviously this gives an unchecked warning about the second line.
    >>>Because of type erasure the third line doesn't cause a run-time
    >>>exception either. What surprises me is that the fourth line doesn't
    >>>cause a class cast exception, despite accessing the Vector through
    >>>s. I presume internally type erasure turns it into:
    >>>

    >>
    >>Why would assigning anything (except a primitive type, which would
    >>cause a compiler error) to Object ever cause a class cast exception?

    >
    >
    >
    > You're missing the OP's point here.


    You are correct.

    >
    > Generics allows you to omit the cast when retrieving values. Given the OP's
    > vector of strings "s":
    >
    > String str = s.get(0);
    >
    > is allowed without requiring the
    >
    > String str = (String)s.get(0);
    >
    > cast that you would have to do pre-generics. That cast is "added in". So
    > the question is what happens in the 4th line of his example:
    >
    > Object p = s.get(0);
    >
    > One would think that this might internally become:
    >
    > Object p = (String)s.get(0);
    >
    > because of "s" being a generic (Vector of String). That is where the CCE
    > might have come in. What he has at slot 0 in the vector is a plain old
    > Object, not a String. So his question is why doesn't this fail.
    >
    >


    Thanks for the exposition.

    Ray

    --
    XML is the programmer's duct tape.
     
    Raymond DeCampo, Aug 29, 2005
    #7
  8. Safalra

    Safalra Guest

    Mike Schilling wrote:
    > "Thomas G. Marshall" <>
    > wrote in message news:lmuQe.6198$fP.1084@trndny08...
    > > [snip]
    > > Object p = s.get(0);
    > > One would think that this might internally become:
    > > Object p = (String)s.get(0);
    > > because of "s" being a generic (Vector of String). That is where the CCE
    > > might have come in. What he has at slot 0 in the vector is a plain old
    > > Object, not a String. So his question is why doesn't this fail.

    >
    > Mine too. Surely if I wrote
    > methodX(s.get(0))
    > and methodX had overloads methodX(String) and methodX(Object), this should
    > generate
    > methodX((String)s.get(0))
    > On the other hand, if methodX only has the overload methodX(Object), the
    > cast in
    > methodX((String)s.get(0))
    > is unnecessary and, it can be argued, should be omitted. Is it the case,
    > then, that the return type implied by the use of the generic method (i.e.
    > "get()") determines whether the cast is applied? That seems evil to me,
    > being the moral equivalent of overloading methods by return type.


    I don't understand in what way you consider these equivalent.

    --
    Safalra (Stephen Morley)
    http://www.safalra.com/programming/
     
    Safalra, Aug 30, 2005
    #8
  9. Mike Schilling coughed up:
    > "Thomas G. Marshall"
    > <> wrote in
    > message news:lmuQe.6198$fP.1084@trndny08...


    ....[rip]...

    >> One would think that this might internally become:
    >>
    >> Object p = (String)s.get(0);
    >>
    >> because of "s" being a generic (Vector of String). That is where
    >> the CCE might have come in. What he has at slot 0 in the vector is
    >> a plain old Object, not a String. So his question is why doesn't
    >> this fail.

    >
    >
    > Mine too. Surely if I wrote
    >
    > methodX(s.get(0))
    >
    > and methodX had overloads methodX(String) and methodX(Object), this
    > should generate
    >
    > methodX((String)s.get(0))
    >
    > On the other hand, if methodX only has the overload methodX(Object),
    > the cast in
    >
    > methodX((String)s.get(0))
    >
    > is unnecessary and, it can be argued, should be omitted. Is it the
    > case, then, that the return type implied by the use of the generic
    > method (i.e. "get()") determines whether the cast is applied? That
    > seems evil to me, being the moral equivalent of overloading methods
    > by return type.


    I'm not thrilled with hardly any of this generics business. As I've said
    before, it really seems the wrong solution, and generics should have been a
    full-blown runtime thing, backward compatibility be damned.

    However, this particular blemish doesn't bother me as much as it seems to
    you: it seems as if it is a compiler simply making a later pass over the
    code and removing broken casts. (maybe).

    That's the problem with half-baked solutions. They tend to necessitate
    plaster and spackle in various places.


    > (And it would cause an odd bug if the overload
    > methodX(String) were added after the caller was compiled) But I
    > can't in all honesty say that I've thought this through.
     
    Thomas G. Marshall, Aug 30, 2005
    #9
  10. Safalra

    Roedy Green Guest

    On 28 Aug 2005 08:58:45 -0700, "Safalra" <> wrote or
    quoted :

    >Vector<String> s=new Vector<String>(10);
    >Vector o=s;
    >o.add(new Object());
    >Object p=s.get(0);


    I would read it this way:

    You have made a deal with Javac. You have said the Vector will
    contain only Strings. If there is any unchecked code, you accept the
    compiler's warning and agree to keep the contract manually to let only
    Strings into the Vector.

    So the compiler then looks at the line

    Object p=s.get(0);

    It says to itself, s contains only Strings. Therefore get must return
    a String. A String is a species of Object, so no cast is needed.

    Further, from a type erasure point of view, s at run time contains
    only Objects (or subclasses) absolutely guaranteed, so no cast is
    necessary.


    --
    Canadian Mind Products, Roedy Green.
    http://mindprod.com Again taking new Java programming contracts.
     
    Roedy Green, Aug 31, 2005
    #10
  11. "Safalra" <> wrote in message
    news:...
    > Mike Schilling wrote:
    >> "Thomas G. Marshall"
    >> <>
    >> wrote in message news:lmuQe.6198$fP.1084@trndny08...
    >> > [snip]
    >> > Object p = s.get(0);
    >> > One would think that this might internally become:
    >> > Object p = (String)s.get(0);
    >> > because of "s" being a generic (Vector of String). That is where the
    >> > CCE
    >> > might have come in. What he has at slot 0 in the vector is a plain old
    >> > Object, not a String. So his question is why doesn't this fail.

    >>
    >> Mine too. Surely if I wrote
    >> methodX(s.get(0))
    >> and methodX had overloads methodX(String) and methodX(Object), this
    >> should
    >> generate
    >> methodX((String)s.get(0))
    >> On the other hand, if methodX only has the overload methodX(Object), the
    >> cast in
    >> methodX((String)s.get(0))
    >> is unnecessary and, it can be argued, should be omitted. Is it the case,
    >> then, that the return type implied by the use of the generic method (i.e.
    >> "get()") determines whether the cast is applied? That seems evil to me,
    >> being the moral equivalent of overloading methods by return type.

    >
    > I don't understand in what way you consider these equivalent.


    In both cases an expression's meaning is being (partially, at least)
    determined by how it's used. That is, from the top down, rather than from
    the bottom up. If s is a Vector<String>, a good, simple rule is that
    s.get(0) is of type String. If it's sometimes of type object, things have
    gotten far more complex. Likewise if foo(x)'s type depends on what's done
    with foo's return value.
     
    Mike Schilling, Aug 31, 2005
    #11
  12. "Roedy Green" <> wrote in message
    news:eek:...
    > On 28 Aug 2005 08:58:45 -0700, "Safalra" <> wrote or
    > quoted :
    >
    >>Vector<String> s=new Vector<String>(10);
    >>Vector o=s;
    >>o.add(new Object());
    >>Object p=s.get(0);

    >
    > I would read it this way:
    >
    > You have made a deal with Javac. You have said the Vector will
    > contain only Strings. If there is any unchecked code, you accept the
    > compiler's warning and agree to keep the contract manually to let only
    > Strings into the Vector.
    >
    > So the compiler then looks at the line
    >
    > Object p=s.get(0);
    >
    > It says to itself, s contains only Strings. Therefore get must return
    > a String. A String is a species of Object, so no cast is needed.


    Top-down rather than bottom-up, expression parsing. Ugh.
     
    Mike Schilling, Aug 31, 2005
    #12
  13. "Chris Uppal" <-THIS.org> wrote in message
    news:4312bee3$2$38041$...
    >
    > BTW, don't forget that the signature of the called method is part of the
    > invokexxx bytecode(s), so if any other methodX(String) were added later,
    > then
    > that would be "invisible" to the code that was compiled before it existed.



    Yeah, I realized that somewhat after I posted. I warned you I hadn't
    thought it through.
     
    Mike Schilling, Aug 31, 2005
    #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. Sebastian Millies

    Java Generics, Type Erasure and Frameworks

    Sebastian Millies, Oct 7, 2004, in forum: Java
    Replies:
    1
    Views:
    656
    Chris Uppal
    Oct 7, 2004
  2. z-man
    Replies:
    8
    Views:
    377
    Thomas Weidenfeller
    Oct 9, 2006
  3. Martin Lorentzson

    Generics and type erasure

    Martin Lorentzson, Nov 5, 2006, in forum: Java
    Replies:
    2
    Views:
    422
    Martin Lorentzson
    Nov 6, 2006
  4. William
    Replies:
    5
    Views:
    1,027
  5. William
    Replies:
    3
    Views:
    658
    Andreas Leitgeb
    Mar 4, 2011
Loading...

Share This Page