Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Java > Generic type cast involving wildcard type

Reply
Thread Tools

Generic type cast involving wildcard type

 
 
Daniel Thoma
Guest
Posts: n/a
 
      07-28-2009
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


 
Reply With Quote
 
 
 
 
Lew
Guest
Posts: n/a
 
      07-28-2009
On Jul 28, 4:49*pm, Daniel Thoma <(E-Mail Removed)> 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
 
Reply With Quote
 
 
 
 
markspace
Guest
Posts: n/a
 
      07-28-2009
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.
 
Reply With Quote
 
Daniel Thoma
Guest
Posts: n/a
 
      07-28-2009
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




 
Reply With Quote
 
markspace
Guest
Posts: n/a
 
      07-28-2009
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.
 
Reply With Quote
 
Daniel Thoma
Guest
Posts: n/a
 
      07-29-2009
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.



 
Reply With Quote
 
Alessio Stalla
Guest
Posts: n/a
 
      07-29-2009
On Jul 28, 10:49*pm, Daniel Thoma <(E-Mail Removed)> 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
 
Reply With Quote
 
Tom Anderson
Guest
Posts: n/a
 
      07-29-2009
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
 
Reply With Quote
 
Daniel Thoma
Guest
Posts: n/a
 
      07-29-2009
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.



 
Reply With Quote
 
Lew
Guest
Posts: n/a
 
      07-30-2009
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
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Wildcard String Comparisons: Set Pattern to a Wildcard Source chaoticcranium@gmail.com Python 7 10-05-2010 09:26 PM
not just generic type programming,but also parallism generic syntaxprogramming?? minlearn C++ 2 03-13-2009 05:17 PM
error C2440: 'return' : cannot convert from 'const char *' to 'const unsigned short *' Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast Abhijit Bhadra C++ 2 12-01-2004 04:43 PM
malloc - to cast or not to cast, that is the question... EvilRix C Programming 8 02-14-2004 12:08 PM
to cast or not to cast malloc ? MSG C Programming 38 02-10-2004 03:13 PM



Advertisments