array casting

D

dev null

Why can't arrays be cast at runtime? Sure, if I mess up and try to
cast an array of, say, Dates into an array of JLabels, then I should
pay the price and get a ClassCastException; but otherwise I would
expect the language to behave as with usual casts. Example:

import java.util.*;

class TryMe
{
public static void main (String args[])
{
Object o[] = {"one", "two", "three"};
// this works but seems a lot klunkier than needs be
String s[] = (String [])(Arrays.asList (o).toArray (new String [] {}));

System.out.println ("class of s: " + s.getClass ());
System.out.println ("class of s[0]: " + s[0].getClass ());

for (int i = 0; i < s.length; i++)
System.out.println ("s[" + s + "] = " + s);

try // this is how I'd like array casting to work
{
String t[] = (String []) o; // line 18
}
catch (Error e)
{
e.printStackTrace ();
}
catch (Exception e)
{
e.printStackTrace ();
}
}
}

output:

class of s: class [Ljava.lang.String;
class of s[0]: class java.lang.String
s[0] = one
s[1] = two
s[3] = three
java.lang.ClassCastException
at TryMe.main(TryMe.java:18)

Also, why can't I catch the ClassCastException?

TIA,
 
A

Adam Maass

dev null said:
Why can't arrays be cast at runtime? Sure, if I mess up and try to
cast an array of, say, Dates into an array of JLabels, then I should
pay the price and get a ClassCastException; but otherwise I would
expect the language to behave as with usual casts. Example:

import java.util.*;

class TryMe
{
public static void main (String args[])
{
Object o[] = {"one", "two", "three"};
// this works but seems a lot klunkier than needs be
String s[] = (String [])(Arrays.asList (o).toArray (new String [] {}));

System.out.println ("class of s: " + s.getClass ());
System.out.println ("class of s[0]: " + s[0].getClass ());

for (int i = 0; i < s.length; i++)
System.out.println ("s[" + s + "] = " + s);

try // this is how I'd like array casting to work
{
String t[] = (String []) o; // line 18
}
catch (Error e)
{
e.printStackTrace ();
}
catch (Exception e)
{
e.printStackTrace ();
}
}
}

output:

class of s: class [Ljava.lang.String;
class of s[0]: class java.lang.String
s[0] = one
s[1] = two
s[3] = three
java.lang.ClassCastException
at TryMe.main(TryMe.java:18)

Also, why can't I catch the ClassCastException?

TIA,


arrays have an element type. So a String[] is distinct from an Object[],
even though a variable of type Object[] can hold a String[].

BTW, you can use this to initialize a specific String[]:

String[] = new String[] {"one", "two", "three"};


Here's the problem with the cast you want to have magically work:

Object[] objs = {"one", "two", "three"};
String[] strs = (String[])objs;

objs[1] = new Integer(1);

strs[1]; // What happens on this line?

It isn't a String, even though the variable says it is. Is it converted to a
String? Does the runtime try to cast it to a String? Do you get an
incompatible type exception?

-- Adam Maass
 
D

dev null

Adam Maass said:
Object[] objs = {"one", "two", "three"};
String[] strs = (String[])objs;
objs[1] = new Integer(1);
strs[1]; // What happens on this line?

It isn't a String, even though the variable says it is. Is it converted to a
String? Does the runtime try to cast it to a String? Do you get an
incompatible type exception?

Adam:

Thanks for your reply.

If array casts worked like usual casts, I'd have no
problem with the JVM tossing a ClassCastException at
runtime at the point where strs[1] is dereferenced in
your above example. That's the same scenario I was
referring to in my original post; namely:

Object o[] = new Object[2];
o[0] = "hi there";
o[1] = new JLabel ("hi there");
String s[] = (String []) o;
int i = s[0].length ();
int j = s[1].length ();

In a universe where array casting is endorsed by Sun
in a similar way as other castings, I'd expect the JVM
to toss a ClassCastException (or some other Exception)
only at the last line where s[1] is dereferenced to an
Object that only then is discovered by the JVM to not
be a String. That seems to be what javac is endorsing
when it compiles my original example. My point is that
Sun has gone half-way in the array casting department;
the compiler will accept a whole range of (sometimes
silly) array casting statements but then "down-casts"
don't seem to work. "Up-casts" are OK though:

class NoProb
{
static public void main (String args[])
{
String s[] = {"one", "two", "three"};
Object o[] = (Object []) s;
System.out.println (o[1].hashCode ());
}
}

The other problem I'm having is that the runtime
ClassCastException cannot be caught. I'll dig out
my copy of the Java Lang Spec and see if this is
defined to be some sort of special, uncatchable case...

Thanks again for your reply,
 
M

Michael Borgwardt

dev said:
If array casts worked like usual casts, I'd have no

It's not not so much that array casts are different from normal casts,
*arrays* are different from normal objects.
problem with the JVM tossing a ClassCastException at
runtime at the point where strs[1] is dereferenced in
your above example.

You might not, but a lot of other people would have very serious problems
with that.
That's the same scenario I was
referring to in my original post; namely:

Object o[] = new Object[2];
o[0] = "hi there";
o[1] = new JLabel ("hi there");
String s[] = (String []) o;
int i = s[0].length ();
int j = s[1].length ();

In a universe where array casting is endorsed by Sun
in a similar way as other castings, I'd expect the JVM
to toss a ClassCastException (or some other Exception)
only at the last line where s[1] is dereferenced to an
Object that only then is discovered by the JVM to not
be a String.

That would be absolutely *horrible*. As it is now, a
ClassCastException can only occur when you're actually
performing a class cast. With your suggestion, it could
crop up whenever you access an object array, even if your
code isn't doing any casting at all! Additionally, it would
mean that the JVM had to perform a type check every time
you access an object array, which would be extremely bad for
performance.

That seems to be what javac is endorsing
when it compiles my original example. My point is that
Sun has gone half-way in the array casting department;
the compiler will accept a whole range of (sometimes
silly) array casting statements

Nope, it only seems silly when put in direct sequence. In the
real world, programmers often write code with no idea who
will end up calling it, and parts of the code are compiled
at different times.
but then "down-casts"
don't seem to work. "Up-casts" are OK though:

class NoProb
{
static public void main (String args[])
{
String s[] = {"one", "two", "three"};
Object o[] = (Object []) s;

You don't need a cast at all for this to work.
The other problem I'm having is that the runtime
ClassCastException cannot be caught.

What gave you that idea?
 
T

Tor Iver Wilhelmsen

Why can't arrays be cast at runtime? Sure, if I mess up and try to
cast an array of, say, Dates into an array of JLabels, then I should
pay the price and get a ClassCastException; but otherwise I would
expect the language to behave as with usual casts.

It does: But the "class" Object[] extends Object and the "class"
String[] extends Object - the two are just as unrelated as Date and
JLabel.

The problem is you're not observing that the arrays are themselves
types too.
 
C

Chris Riesbeck

Adam Maass said:
Object[] objs = {"one", "two", "three"};
String[] strs = (String[])objs;
objs[1] = new Integer(1);
strs[1]; // What happens on this line?

If array casts worked like usual casts, I'd have no
problem with the JVM tossing a ClassCastException at
runtime at the point where strs[1] is dereferenced in
your above example.

If you delayed typechecking until reference time,
every reference to an object's methods or fields would
have to be typechecked first at runtime. You can
do that (Lisp does if types aren't declared) but
there's an efficiency cost and safety loss
that Java's designers weren't prepared to pay.
"Up-casts" are OK though:

Up-casts are always safe.
The other problem I'm having is that the runtime
ClassCastException cannot be caught.

try {
Object x = "abc";
Integer y = (Integer) x;
}
catch (ClassCastException e) {
System.out.println("caught it");
}

works fine for me. What are you trying to do?
 
J

Joona I Palaste

Andrew Cowper said:
Tor Iver Wilhelmsen said:
(e-mail address removed) (dev null) writes:
It does: But the "class" Object[] extends Object and the "class"
String[] extends Object - the two are just as unrelated as Date and
JLabel.
Not exactly. String[] is a subclass of Object[] as well as a subclass
of Object.
public class Bosh {
public static void main (String[] args) {
Object[] x = new Object[] { new Integer(2), "hello" };
String[] y = new String[] { "hello", "world" };
System.out.println("1: " + (x instanceof Object));
System.out.println("2: " + (y instanceof Object));
System.out.println("3: " + (y instanceof Object[]));
}
}
bash-2.05a$ java -cp . Bosh
1: true
2: true
3: true

I've just tried the obvious question out myself.

public class Test {
public static void main(String[] args) {
Object[] objectArray = new String[] { "hello", "world" };
String[] stringArray = (String[])objectArray;
objectArray[1] = new Integer(1);
System.out.println(stringArray[1].length());
}
}

This causes a java.lang.ArrayStoreException at the line where I'm
assigning new Integer(1) to objectArray[1], but only at run-time,
not at compile-time.
Removing the lines with the array stringArray did not affect the
outcome. I guess this is Java's way of guarding itself against
run-time mismatch in method bindings.

--
/-- Joona Palaste ([email protected]) ---------------------------\
| Kingpriest of "The Flying Lemon Tree" G++ FR FW+ M- #108 D+ ADA N+++|
| http://www.helsinki.fi/~palaste W++ B OP+ |
\----------------------------------------- Finland rules! ------------/
"I am lying."
- Anon
 
A

Andrew Cowper

Tor Iver Wilhelmsen said:
(e-mail address removed) (dev null) writes:
It does: But the "class" Object[] extends Object and the "class"
String[] extends Object - the two are just as unrelated as Date and
JLabel.

Not exactly. String[] is a subclass of Object[] as well as a subclass
of Object.

public class Bosh {
public static void main (String[] args) {
Object[] x = new Object[] { new Integer(2), "hello" };
String[] y = new String[] { "hello", "world" };

System.out.println("1: " + (x instanceof Object));
System.out.println("2: " + (y instanceof Object));
System.out.println("3: " + (y instanceof Object[]));
}
}

bash-2.05a$ java -cp . Bosh
1: true
2: true
3: true
 
C

Chris Uppal

Andrew said:
Not exactly. String[] is a subclass of Object[] as well as a subclass
of Object.

Actually, this isn't quite true. String[] and Object[] are unrelated classes
that are both direct subclasses of Object.

The compiler does let you use a String[] in a context where an Object[] is
expected, but that's because of a designed-in wart in Java's type system, not
because of inheritance.

Which, BTW, means that the "instanceof" operator is misleadingly named -- it's
doing a test more like the java.lang.Class method, isAssignableFrom().

-- chris
 
J

John C. Bollinger

dev said:
Object[] objs = {"one", "two", "three"};
String[] strs = (String[])objs;
objs[1] = new Integer(1);
strs[1]; // What happens on this line?

It isn't a String, even though the variable says it is. Is it converted to a
String? Does the runtime try to cast it to a String? Do you get an
incompatible type exception?


Adam:

Thanks for your reply.

If array casts worked like usual casts, I'd have no
problem with the JVM tossing a ClassCastException at
runtime at the point where strs[1] is dereferenced in
your above example.

Java array casting does work like usual Java casts. The issue that you
may not be appreciating is that java arrays are full-fledged objects in
their own right, with classes distinct from the classes of their
components. Thus the expression strs[1] in Adam's example is not
fundamentally a dereference (although the reference strs must be
resolved to perform the evaluation) but rather an operation on the
object referenced by strs. Conceptually that operation is a method
invocation, but a JVM might or might not have a special purpose path for
this particular kind of operation.

An object of class Object cannot successfully be cast to type String, so
why would you expect an object of class Object[] to be successfully cast
to type String[]? The actual classes of the array components are
irrelevant to the question, but the fact that they may not be Strings is
central: every String[] is an Object[], but Object[]s are not
necessarilly String[]s, even if all their components are in fact Strings.

Not only is this treatment conceptually consistent, it is quite
convenient. I only have to worry about ClassCastExceptions where I
actually perform the cast, not at every (explicit and implicit)
retrieval of an array component. If the approach you seem to want were
adopted then Java programs could throw ClassCastExceptions almost
anywhere, because object arrays of various kinds are used behind the
scenes in many of the platform API.
That's the same scenario I was
referring to in my original post; namely:

Object o[] = new Object[2];
o[0] = "hi there";
o[1] = new JLabel ("hi there");
String s[] = (String []) o;
int i = s[0].length ();
int j = s[1].length ();

In a universe where array casting is endorsed by Sun
in a similar way as other castings, I'd expect the JVM
to toss a ClassCastException (or some other Exception)
only at the last line where s[1] is dereferenced to an
Object that only then is discovered by the JVM to not
be a String. That seems to be what javac is endorsing
when it compiles my original example. My point is that
Sun has gone half-way in the array casting department;
the compiler will accept a whole range of (sometimes
silly) array casting statements but then "down-casts"
don't seem to work.

The compiler is not required to diagnose every situation that, in
principle, it could recognize as certain to cause a runtime exception.
In fact, I'm not sure the compiler is required to diagnose any such
situation. In your above example, for instance, the compiler could
recognize that reference variable o was initialized with a reference to
an object of class Object[], and then complain about the cast to
String[]. HOWEVER, some Object[]s _are_ String[]s, so unless the
compiler performs potentially complex dataflow analysis it cannot
determine when it compiles the cast statement whether or not the cast
would cause an exception.
"Up-casts" are OK though:

class NoProb
{
static public void main (String args[])
{
String s[] = {"one", "two", "three"};
Object o[] = (Object []) s;
System.out.println (o[1].hashCode ());
}
}

Upcasts are always OK, and can always be verified at compile-time. They
are basically a no-op at runtime.
The other problem I'm having is that the runtime
ClassCastException cannot be caught. I'll dig out
my copy of the Java Lang Spec and see if this is
defined to be some sort of special, uncatchable case...

ClassCastException is in no way special with regard to being caught.
Any Throwable can be caught. Most likely you are getting such an
exception other than where you anticipate, outside the scope of any try
block. You might even be getting multiple such exceptions, one or more
of them caught, and another not. For instance, you might be getting one
thrown from the catch block of your exception handler.


John Bollinger
(e-mail address removed)
 

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