Generic type cast involving wildcard type

Discussion in 'Java' started by Daniel Thoma, Jul 28, 2009.

  1. Daniel Thoma

    Daniel Thoma Guest

    Hi!

    I can't figure out, why casting to a subclass causes an unchecked cast
    warning, when a wildcard type is involved, but seems to work in any other
    case:

    Example:
    static class Base<T> { }

    static class Derived<T> extends Base<T> { }

    <T> void foo() {
    Base<? extends CharSequence> base = new Derived<String>();
    Derived<? extends CharSequence> derived
    = (Derived<? extends CharSequence>) base; // causes warning

    Base<T> base2 = new Derived<T>();
    Derived<T> derived2 = (Derived<T>) base2;

    Base<String> base3 = new Derived<String>();
    Derived<String> derived3 = (Derived<String>) base3;
    }

    Compiler warning:
    warning: [unchecked] unchecked cast
    found : test.Main.Base<capture#325 of ? extends java.lang.CharSequence>
    required: test.Main.Derived<? extends java.lang.CharSequence>
    Derived<? extends CharSequence> derived = (Derived<? extends CharSequence>)
    base;

    Is there an explanation for this behavior?

    Daniel
    Daniel Thoma, Jul 28, 2009
    #1
    1. Advertising

  2. Daniel Thoma

    Lew Guest

    On Jul 28, 4:49 pm, Daniel Thoma <> wrote:
    > Hi!
    >
    > I can't figure out, why casting to a subclass causes an unchecked cast
    > warning, when a wildcard type is involved, but seems to work in any other
    > case:
    >
    > Example:
    > static class Base<T> { }
    >
    > static class Derived<T> extends Base<T> { }
    >
    > <T> void foo() {
    >   Base<? extends CharSequence> base = new Derived<String>();
    >   Derived<? extends CharSequence> derived
    >         = (Derived<? extends CharSequence>) base; // causes warning
    >
    >   Base<T> base2 = new Derived<T>();
    >   Derived<T> derived2 = (Derived<T>) base2;
    >
    >   Base<String> base3 = new Derived<String>();
    >   Derived<String> derived3 = (Derived<String>) base3;
    >
    > }
    >
    > Compiler warning:
    > warning: [unchecked] unchecked cast
    > found   : test.Main.Base<capture#325 of ? extends java.lang.CharSequence>
    > required: test.Main.Derived<? extends java.lang.CharSequence>
    >         Derived<? extends CharSequence> derived = (Derived<? extends CharSequence>)
    >         base;
    >
    > Is there an explanation for this behavior?


    Casting is a runtime operation. By run time, all generics information
    has been erased, so an attempt to do a generics-aware cast will yield
    an "unchecked" warning. This is true for all generic casts except
    those involving an unbound wildcard.

    Part of the rationale is that such casts cannot be proven safe at
    compile time.

    A workaround is to use '@SuppressWarnings( "unchecked" )' at the
    declaration of
    'Derived<? extends CharSequence> derived'.

    This annotation must be used most carefully only in places where you
    can demonstrate conclusively that it's safe to do so, and you should
    document that demonstration in a comment at the point of annotation.
    If you make a mistake, you will get a 'ClassCastException' at runtime,
    just the sort of problem generics were designed to prevent.

    A better solution would be to complete your type analysis so that the
    downcast is not needed.

    For details and background, see:
    <http://java.sun.com/docs/books/tutorial/extra/generics/index.html>
    <http://java.sun.com/docs/books/effective/generics.pdf>

    --
    Lew
    Lew, Jul 28, 2009
    #2
    1. Advertising

  3. Daniel Thoma

    markspace Guest

    Daniel Thoma wrote:

    > Is there an explanation for this behavior?



    I think it's because generics aren't reified. In other words, there's
    no way for the compiler to test that "base" actually does parameterize
    <? extends CharSequence>. All it can do is test that "base" is of type
    "Derived" (which basically means Derived<?>) and that's it. You only
    have raw types at runtime.

    Thus the compiler is just telling you that you're getting a pig in a
    poke, and asks that you acknowledge that fact with a little
    @SuppressWarnings.
    markspace, Jul 28, 2009
    #3
  4. Daniel Thoma

    Daniel Thoma Guest

    Hi Lew,

    thank's for your answer.

    > Casting is a runtime operation. By run time, all generics information
    > has been erased

    Yes, I'm aware of that.

    > This is true for all generic casts except those involving an unbound

    wildcard.
    That's know the whole truth, a generic cast causes a warning, only if
    generic type information would be neccessary to check the cast at runtime.

    These two examples work fine, because the cast only affects the class, not
    the type parameters:
    >> Base<T> base2 = new Derived<T>();
    >> Derived<T> derived2 = (Derived<T>) base2;
    >>
    >> Base<String> base3 = new Derived<String>();
    >> Derived<String> derived3 = (Derived<String>) base3;

    The type information isn't available at runtime, but the compiler can infer,
    that the type parameters will always have the required type.

    So my question is, why isn't that possible, when using wildcard types:
    > Base<? extends CharSequence> base = new Derived<String>();
    > Derived<? extends CharSequence> derived
    > = (Derived<? extends CharSequence>)base;

    The compiler should be able to infer, that the type-variable has always the
    lower bound CharSequence.

    Daniel
    Daniel Thoma, Jul 28, 2009
    #4
  5. Daniel Thoma

    markspace Guest

    Daniel Thoma wrote:

    > The compiler should be able to infer, that the type-variable has always the
    > lower bound CharSequence.



    I don't think it can. That's the issue. When you do this:

    derived = (Derived<...>) base;

    The only part of that that the runtime can actually check is the
    (Derived) part. In other words, because a cast is an actual runtime
    operation, and the cast can only check part of what it is being asked to
    check, it issues a warning.

    That's different from something like:

    Derived<?> derived = new Base<String>();

    Which needs no runtime checks, it's all done at compile time.
    markspace, Jul 29, 2009
    #5
  6. Daniel Thoma

    Daniel Thoma Guest

    You mean something like this:
    > Derived<?> derived = (Derived<?>) new Base<String>();


    But why is it then, that something like this works:
    Derived<String> derived = (Derived<String>) new Base<String>();

    The compiler can obviously sometimes infer, that a generic cast can be
    savely "simplified" to a normal cast.
    If that doesn't work with wildcard types, there has to be some reason for
    that. It's either a compiler bug, a specified limitation of the language or
    there has to be an example, where such a cast would be unsafe, i.e. the cast
    could cause a ClassCastException at some later point in the program, like in
    this, obvious case:

    List<?> list = new List<Object>();
    List<String> list2 = (List<String>) list1; //unsafe warning
    String s = list2.get(0); //May cause ClassCastException

    Lets consider this example:
    List<? extends CharSequence> list = ...
    ArrayList<? extends CharSequence> list2
    = (ArrayList<? extends CharSequence>) list
    CharSequence s = list2.get(0);

    Is there something, I could insert for "..." in the first line, such that I
    get a ClassCastException in the last line.
    Daniel Thoma, Jul 29, 2009
    #6
  7. On Jul 28, 10:49 pm, Daniel Thoma <> wrote:
    > Hi!
    >
    > I can't figure out, why casting to a subclass causes an unchecked cast
    > warning, when a wildcard type is involved, but seems to work in any other
    > case:
    >
    > Example:
    > static class Base<T> { }
    >
    > static class Derived<T> extends Base<T> { }
    >
    > <T> void foo() {
    >   Base<? extends CharSequence> base = new Derived<String>();
    >   Derived<? extends CharSequence> derived
    >         = (Derived<? extends CharSequence>) base; // causes warning
    >
    >   Base<T> base2 = new Derived<T>();
    >   Derived<T> derived2 = (Derived<T>) base2;
    >
    >   Base<String> base3 = new Derived<String>();
    >   Derived<String> derived3 = (Derived<String>) base3;
    >
    > }
    >
    > Compiler warning:
    > warning: [unchecked] unchecked cast
    > found   : test.Main.Base<capture#325 of ? extends java.lang.CharSequence>
    > required: test.Main.Derived<? extends java.lang.CharSequence>
    >         Derived<? extends CharSequence> derived = (Derived<? extends CharSequence>)
    >         base;
    >
    > Is there an explanation for this behavior?


    I believe the explanation lies in the correct interpretation of the ?
    wildcard; I think I got struck by that too.

    ? extends X does not mean "any class that extends X", but "a given
    class that extends X, which one I don't know".

    So when you use <T> or <String> in your cast, the compiler knows
    you're referring to the very same class; when you use <? extends
    CharSequence>, the compiler understands that you're referring to two
    classes that extend CharSequence, which might or might not be the same
    one.

    You cannot cast from List<T extends X> to List<S extends X>, and
    using ? is just like using T or S, except that you don't name the
    type.

    --
    Alessio
    Alessio Stalla, Jul 29, 2009
    #7
  8. Daniel Thoma

    Tom Anderson Guest

    On Tue, 28 Jul 2009, Daniel Thoma wrote:

    > I can't figure out, why casting to a subclass causes an unchecked cast
    > warning, when a wildcard type is involved, but seems to work in any other
    > case:
    >
    > Example:
    > static class Base<T> { }
    >
    > static class Derived<T> extends Base<T> { }
    >
    > <T> void foo() {
    > Base<? extends CharSequence> base = new Derived<String>();
    > Derived<? extends CharSequence> derived
    > = (Derived<? extends CharSequence>) base; // causes warning
    >
    > Base<T> base2 = new Derived<T>();
    > Derived<T> derived2 = (Derived<T>) base2;
    >
    > Base<String> base3 = new Derived<String>();
    > Derived<String> derived3 = (Derived<String>) base3;
    > }
    >
    > Compiler warning:
    > warning: [unchecked] unchecked cast
    > found : test.Main.Base<capture#325 of ? extends java.lang.CharSequence>
    > required: test.Main.Derived<? extends java.lang.CharSequence>
    > Derived<? extends CharSequence> derived = (Derived<? extends CharSequence>)
    > base;
    >
    > Is there an explanation for this behavior?


    Yes - you can't cast from one question mark to the other, because they're
    different question marks.

    Seriously.

    There's no explicit link between the "? extends CharSequence" on base and
    the "? extends CharSequence" on derived. Thus, they're different
    'captures' of the "? extends CharSequence" wildcard (as subtly hinted in
    the error message). As far as the compiler is concerned, those ?s could
    refer to quite different types (remember a ? means 'some specific type,
    but one which isn't fixed at compile time'), and so that cast isn't
    guaranteed to work (and can't be checked at runtime, because java's
    generics aren't reified).

    You can fix this by adding a type parameter to the method - declare <Q
    extends CharSequence> and replace "? extends CharSequence" with Q.

    Although in your example, this won't work, because you then try to assign
    a Derived<String> to it, which is obviously no good, because there is no
    static guarantee that Q includes String. If you change base to being a
    method parameter rather than a local, thus magically ignoring the question
    of its actual value, it compiles cleanly.

    This is quite arcane stuff, which is why all the answers you've had up to
    Alessio's have got it completely wrong!

    tom

    --
    Teach us how to die well
    Tom Anderson, Jul 29, 2009
    #8
  9. Daniel Thoma

    Daniel Thoma Guest

    First of all, I found an interesting workaround:

    Base<? extends CharSequence> base4 = new Derived<String>();
    Derived<? extends CharSequence> derived4 = cast(base4);

    <U> Derived<U> cast(Base<U> base) {
    return (Derived<U>) base;
    }

    This example causes no unchecked warning. I now tried to read the JLS in
    more detail and came up with the following explanation:

    In the second line on the evaluation of base4 capure conversion is applied
    (JLS 5.1.10) which results in a type captureX extends CharSequence].
    Now typinference yields U = captureX. Therefore case(base4) has has a return
    type of Derived<captureX>. Derived<captureX> is a subtype of Derived<?
    extends CharSequence> (JLS 4.10.2), therefore the result can be assigned to
    derived4.

    As I understand the JLS, capture conversion is never carried out for the
    type of derived4.
    The direct cast should behave any different. Capture-conversion will be
    applied to the cast-expression, resulting in a type Derived<captureX>. The
    compiler message supports this, since it only mentions a capture variable
    for the "found"-part.
    So the question to me is, why is Derived<captureX> assingable to Derived<?
    extends CharSequence> in case of the method invokation but not in case of
    the direct cast.
    Daniel Thoma, Jul 29, 2009
    #9
  10. Daniel Thoma

    Lew Guest

    Daniel Thoma wrote:
    > First of all, I found an interesting workaround:
    >
    > Base<? extends CharSequence> base4 = new Derived<String>();
    > Derived<? extends CharSequence> derived4 = cast(base4);
    >
    > <U> Derived<U> cast(Base<U> base) {
    > return (Derived<U>) base;
    > }
    >
    > This example causes no unchecked warning. I now tried to read the JLS in
    > more detail and came up with the following explanation:
    >
    > In the second line on the evaluation of base4 capure conversion is applied
    > (JLS 5.1.10) which results in a type captureX extends CharSequence].
    > Now typinference yields U = captureX. Therefore case(base4) has has a return
    > type of Derived<captureX>. Derived<captureX> is a subtype of Derived<?
    > extends CharSequence> (JLS 4.10.2), therefore the result can be assigned to
    > derived4.
    >
    > As I understand the JLS, capture conversion is never carried out for the
    > type of derived4.
    > The direct cast should behave any different. Capture-conversion will be
    > applied to the cast-expression, resulting in a type Derived<captureX>. The
    > compiler message supports this, since it only mentions a capture variable
    > for the "found"-part.
    > So the question to me is, why is Derived<captureX> assingable to Derived<?
    > extends CharSequence> in case of the method invokation [sic] but not in case of
    > the direct cast.


    Because the non-wildcard type parameter U captures the exact capture of the
    'base' parameter and pins it to the method return's type parameter, and then
    when you assign the return value to the wildcarded variable that variable in
    turn is able to pick up on this same capture, but in the process effectively
    "forgetting" which capture it picked up. I think of the method as "stepping
    down" the wildcard to a specific capture, then the assignment of the return
    value to a variable as "stepping up" the capture to a wildcard. In between,
    the method ensures a clean pass-through of the type.

    Without the method the compiler doesn't have enough information to guarantee
    the compatibility of the step-down and step-up. I've seen similar idioms in
    the literature for Java generics, whereby a helper method nails down the type
    parameter.

    --
    Lew
    Lew, Jul 30, 2009
    #10
  11. Daniel Thoma

    Daniel Thoma Guest

    What I know from literature is something like this:

    List<?> list = new List<String>();

    list.add(list.get(0)); // fails

    foo(list); // works
    void <T> foo(List<T> list) {
    list.add(list.get(0));
    }

    And in this case, I can follow, because the compiler would have to transport
    the information, that he is dealing with one list (with a fixed but unknown
    type) in both parts of the expression. And it is not so easy to guarantee,
    that "list" doesn't change its value in between.
    In the casting scenario I can not realy follow that explanation. As I see
    it, there aren't even two capture variables involved.
    Daniel Thoma, Jul 30, 2009
    #11
  12. Daniel Thoma

    Tom Anderson Guest

    On Thu, 30 Jul 2009, Daniel Thoma wrote:

    > What I know from literature is something like this:
    >
    > List<?> list = new List<String>();
    >
    > list.add(list.get(0)); // fails
    >
    > foo(list); // works
    > void <T> foo(List<T> list) {
    > list.add(list.get(0));
    > }
    >
    > And in this case, I can follow, because the compiler would have to transport
    > the information, that he is dealing with one list (with a fixed but unknown
    > type) in both parts of the expression. And it is not so easy to guarantee,
    > that "list" doesn't change its value in between.


    If it's a local variable, it's pretty easy, and the kind of thing
    compilers do all the time. Less so if it's an instance variable, true.

    > In the casting scenario I can not realy follow that explanation. As I see
    > it, there aren't even two capture variables involved.


    There aren't - there are three.

    List<?> high = new ArrayList<String>();
    // ^ one
    ArrayList<?> low = (ArrayList<?>)high;
    // ^ two ^ three

    I think the compiler could work out that this is safe - it could look at
    the relatioship between the type variable in ArrayList and the one in
    List, see that they're defined to be the same (by "ArrayList<E> implements
    List<E>"), and deduce that the variables on high and low are the same.

    I don't know that this would be a good idea, though. If you have two
    variables bound to the same type, why not make that explicit by
    introducing a type variable?

    List<Q> high = new ArrayList<String>();
    ArrayList<Q> low = (ArrayList<Q>)high;

    The problem with this is that the only way to define a type variable is to
    add it to the method signature. What would be useful is if we could
    define them locally. Like:

    type <Q>;
    List<Q> high = new ArrayList<String>();
    ArrayList<Q> low = (ArrayList<Q>)high;

    Or something. Or maybe scoped to blocks (weird syntax alert!):

    <Q> {
    List<Q> high = new ArrayList<String>();
    ArrayList<Q> low = (ArrayList<Q>)high;
    }

    Typically, though, this is a non-issue, since the ?-typed thing you're
    operating on is coming in as a method parameter or something anyway, and
    you can set up a type variable there.

    The example code you posted was obviously contrived. What was your real
    code? Can we see how we might solve the real problem?

    tom

    --
    I prefer gin now sleep doesn't want me anyway.
    Tom Anderson, Jul 31, 2009
    #12
  13. Daniel Thoma

    markspace Guest

    Tom Anderson wrote:

    >
    > There aren't - there are three.
    >
    > List<?> high = new ArrayList<String>();
    > // ^ one
    > ArrayList<?> low = (ArrayList<?>)high;
    > // ^ two ^ three
    >



    Thanks for posting this. You gave a great explanation on how wildcard
    capture works. I was always a bit fuzzy on what is going on here. I
    think I've managed to grok it better now.
    markspace, Jul 31, 2009
    #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. MSG

    to cast or not to cast malloc ?

    MSG, Feb 6, 2004, in forum: C Programming
    Replies:
    38
    Views:
    1,055
    Dan Pop
    Feb 10, 2004
  2. EvilRix
    Replies:
    8
    Views:
    618
    Martin Dickopp
    Feb 14, 2004
  3. minlearn
    Replies:
    2
    Views:
    445
    red floyd
    Mar 13, 2009
  4. Replies:
    7
    Views:
    817
  5. Alf P. Steinbach
    Replies:
    0
    Views:
    124
    Alf P. Steinbach
    Nov 12, 2013
Loading...

Share This Page