Java type-casting -- Q1

G

grz01

Hi,

I am trying to get some more grips on Java type-casting,
and have some questions... here the first one:

Compiling in Eclipse...

import java.util.ArrayList;
import java.util.List;

...

String s = "";
Object o = s;

List<String> sList = new ArrayList<String>();
List<Object> oList = (List<Object>)sList;

The first two assignments work fine, of course.

But the last assignment give me an error:

Cannot cast from List<String> to List<Object>

Can someone explain why?
And what is the correct formulation of the offending stmt?

/ grz01
 
G

grz01

    List<String> sList = new ArrayList<String>();
    List<Object> oList = (List<Object>)sList;

the last assignment give me an error:

    Cannot cast from List<String> to List<Object>

I discovered, however, that this works:

List<String> sList = new ArrayList<String>();
List<?> oList = sList;

What's the significant difference between the two?

Any good articles on these issues you can point me to?
 
M

markspace

grz01 said:
But the last assignment give me an error:

Cannot cast from List<String> to List<Object>

Can someone explain why?


I'm going to change Peter's answer. It's not because generics are
unreifiable, it's by design.

We're used to polymorphic behavior where a subclass can be treated as a
superclass, but generics don't work that way.

Normal polymorphism is "covariant." Generics are "invariant." You're
not supposed to be able to assign one type to another.

And what is the correct formulation of the offending stmt?


It depends what you want to accomplish. One trick that many people
forget is that while generics are invariant, the underlying type system
is still covariant.

List<Object> oList = new ArrayList<Object>();
// put a String in
oList.add( "Fred" );

You can still use any subtype of Object as type in list. You can only
get Objects out, but you could test their runtime type, if you must.
This works better if you're actually a bit further down the inheritance
tree than Object. I.e., there's a restricted set of classes that could
go in the list.

There may be better ways of doing this, but "it depends." We'll need
some context and maybe an example (SSCCE) too.
 
J

Joshua Cranmer

I discovered, however, that this works:

List<String> sList = new ArrayList<String>();
List<?> oList = sList;

What's the significant difference between the two?

The best way to approach a generics error or warning is often to think
about "how would allowing this action cause type safety to break?"

In your first example, you tried treating a List<String> as a
List<Object>, which is likely a natural inclination, since a String is a
Object. But it really isn't right:

List<String> sl = new ArrayList<String>();
List<Object> ol = sl; // Let's pretend this worked
ol.add(new Integer(5)); // Integers are also Objects...
sl.get(0); // but they're not Strings!

You should now see why allowing the simple cast is not so good of an
idea. If you're smart, you'll retort that you can do the cast with
arrays. But it's not quite the same thing:

String[] sa = new String[1];
Object[] oa = sa; // This works, of course
oa[0] = new Integer(5); // The compiler doesn't complain here.
String s = sa[0];

The compiler doesn't complain, but the runtime sure does. When you try
storing the integer, you get an ArrayStoreException. Compare this to the
generics case, where you get a ClassCastException at the point of retrieval.

The solution that Java come up with was wildcard generics. How these
exactly work is outside the scope of the post, but the type system rules
and method resolution rules are set up to avoid the possibility that I
described here:

List<String> sl = new ArrayList<String>();
List<? extends Object> wol = sl; // Same as List<?>, but being explicit
wol.add(new Integer(5)); // Compiler says "no such method" [1]
Object o = wol.get(0); // No problems here

[1] Unfortunately, javac seems to love to give the same error message in
response to errant misspellings as errant type conversions.
 
L

Lew

That is because you can only cast up to a supertype or down to a subtype, and

Peter said:
The "?" is a wildcard, allowing the generic type to be described as a
base type of a more specific type.

The wildcard stands for "any subtype / supertype of X", depending on the
presence of keywords "extends" or "super". The wildcard by itself stands for
"any subtype of Object". Hence, 'List<?>' can be a 'List' of "any subtype of
Object", and 'String' qualifies. Thus 'List<String>' is a valid subtype
(well, sort of) of 'List<?>'.

Peter said:

Here's a good article that I've recommended two or three times in this
newsgroup in the last few days:
<http://java.sun.com/docs/books/effective/>
download the free chapter on generics
<http://java.sun.com/docs/books/effective/generics.pdf>

That chapter is a must-read for any Java programmer who wishes to make
effective use of Java generics.

Here is a two-parter by Brian Goetz:
<http://www.ibm.com/developerworks/java/library/j-jtp04298.html>
<http://www.ibm.com/developerworks/java/library/j-jtp07018.html>
 
R

Roedy Green

List<String> sList = new ArrayList<String>();
List<Object> oList = (List<Object>)sList;

Others may offer a more sophisticated explanation. What if Java let
you do that? You could then add general Objects e.g. Apples to an
ArrayList<String>, which would violate the integrity of the ArrayList.
Since these generic type restrictions are not enforced at run time,
the compiler has to detect the problem during compilation. It can't
wait to see if you actually do try to break the integrity.
--
Roedy Green Canadian Mind Products
http://mindprod.com

"Don’t worry about people stealing an idea; if it’s original, you’ll have to shove it down their throats."
~ Howard Aiken (born: 1900-03-08 died: 1973-03-14 at age: 73)
 
D

Daniel Pitts

grz01 said:
I discovered, however, that this works:

List<String> sList = new ArrayList<String>();
List<?> oList = sList;

What's the significant difference between the two?

Assume casting to List<Object> worked, here is what happens:
List<String> sList = new ArrayList<String>();
List<Object> oList = (List<Object>)oList;

oList.add(new Integer(3)); // compiles fine because oList takes Objects
String r = sList.get(0); // Compiles fine, but causes
//a ClassCastException.
Any good articles on these issues you can point me to?

In your second example:
List<String> sList = new ArrayList<String>();
sList.add("Hello");
List<?> oList = sList;

Object o = oList.get(); // Compiles fine, o = "Hello"
oList.add(new Integer(3)); // Fails at compile time

One general rule that I've found is:
When you only read from a structure, declare it <? super Type>.
When you only write to a structure, declare it <? extends Type>.
When you read/write to a structure, declare it <Type>.

For example:
public void processNumbers(List<? extends Number> numberList);

public void createRandomIntegers(Collection<? super Integer> target);
public <T> void reverse(List<T> list);
 
L

Lew

Daniel said:
One general rule that I've found is:
When you only read from a structure, declare it <? super Type>.
When you only write to a structure, declare it <? extends Type>.
When you read/write to a structure, declare it <Type>.

Brian Goetz, in "Going Wild with Generics, Part 2: The get-put
principle"
The get-put principle, as stated in Naftalin and Wadler's fine book on generics,
/Java Generics and Collections/ (see Resources), says:

Use an extends wildcard when you only get values out of a structure,
use a super wildcard when you only put values into a structure,
and don't use a wildcard when you do both.

I think I'll believe Messrs. Goetz, Naftalin and Wadler on this one.
 
M

markspace

Lew said:
Brian Goetz, in "Going Wild with Generics, Part 2: The get-put
principle"


I think I'll believe Messrs. Goetz, Naftalin and Wadler on this one.


Yup, I think Daniel made a minor mistake there.

Joshua Bloch uses the mnemonic PECS: "Producer extends, consumer super."
(He also quotes Naftalin and Wadler's _Get and Put Principle_).

You read from a producer, and you write to a consumer. Mr. Bloch gives
this example:

//Wildcard type for parameter that serves as an E consumer
public void popAll( Collection<? super E> dst ) {
while( ! isEmpty() )
dst.add( pop() );
}

So that should be pretty clear that "super" is the one you write too.
 
D

Daniel Pitts

Lew said:
Brian Goetz, in "Going Wild with Generics, Part 2: The get-put
principle"


I think I'll believe Messrs. Goetz, Naftalin and Wadler on this one.
Indeed, That is what I *meant* to say. It was an unfortunate reversal of
my intent :)

Read: <? extends Type>
Write: <? super Typer>
Both: <Type>

I apologize for leading anyone astray.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top