aspose file tools*
The moose likes Java in General and the fly likes Casting Object[] into String[] Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Java in General
Bookmark "Casting Object[] into String[]" Watch "Casting Object[] into String[]" New topic
Author

Casting Object[] into String[]

Daniel Bauer
Ranch Hand

Joined: Apr 15, 2006
Posts: 49
My question starts with the toArray() method of the Collection - from this I get an Object[] back. However I know that each of the elements in the Collection is a String object, so I figured the obvious thing to do would be to cast it so:

Which as I quickly discovered doesn't work, giving a runtime ClassCastException.

I know there is an alternative method in the Collection where you specify the type:

but you have to admit that is pretty ugly and my question goes a little deeper than that.

Why *can't* you just cast an Object[] into a String[] if all of the elements in the array are actually Strings? The reverse cast (from String[] to Object[]) works as expected. Let's say there is no Collection involved, you already received an Object[] from somewhere and wanted to pass it on to another method which accepts String[]. Is the only way to do this to create a new array, loop over the elements and copy and cast each one in turn? Sounds a bit mad.

Maybe this is much easier with the generics in 1.5, I haven't looked too deeply at that because this particular project has to be 1.4. So I'm trying to deepen my understanding - I was a little shocked that the simple cast doesn't work the way I expected it to.
Paul Sturrock
Bartender

Joined: Apr 14, 2004
Posts: 10336


Why *can't* you just cast an Object[] into a String[] if all of the elements in the array are actually Strings?

Ahhh but you can:




JavaRanch FAQ HowToAskQuestionsOnJavaRanch
Daniel Bauer
Ranch Hand

Joined: Apr 15, 2006
Posts: 49
Well that's more or less what my second code example was doing, just that the one-liner has an extra array instantiation.

But if I have a normal Object array then the cast doesn't work:

This just gives a ClassCastException.

And if I get my Object array from the other Collection.toArray() method, the cast to String[] doesn't work either.

So perhaps my question should be: why is it apparently not possible to cast an Object array to String array (even if all the elements in the array are Strings) unless the Object array is obtained from the Collection.toArray(Object[]) method? Does that method have some special way of creating the array?
Henry Wong
author
Sheriff

Joined: Sep 28, 2004
Posts: 18988
    
  40

So perhaps my question should be: why is it apparently not possible to cast an Object array to String array (even if all the elements in the array are Strings) unless the Object array is obtained from the Collection.toArray(Object[]) method? Does that method have some special way of creating the array?


You have to understand that there are two different (group of) objects being returned here. The array object, and the objects in the collections which are stored in that array.

I guess the *ideal* solution would have been to traverse the collection, looking for the base common object, and create an array of that common object. But they didn't do that.

So, by default, if you collection has only strings, you get an Object array holding String objects, instead of a String array holding String objects.

Henry


Books: Java Threads, 3rd Edition, Jini in a Nutshell, and Java Gems (contributor)
Paul Sturrock
Bartender

Joined: Apr 14, 2004
Posts: 10336


Does that method have some special way of creating the array?

Not special, but different. toArray() just creates an Object array, copies values into it and returns. Basically something like:


toArray(new Object[0]) does the same sort of thing, except it uses the type of the object array passed to create the new array, by calling java.lang.reflect.Array.newInstance().

As to your question why it works this way I can't guess. Given you can do this:

You'd imagine you could do this:


One of life's little mysteries perhaps?
Daniel Bauer
Ranch Hand

Joined: Apr 15, 2006
Posts: 49
Thanks for your replies, that does start to make a little more sense now.
I have noticed that if I get an Object[] from an internal method like this:Then I can indeed cast the returned Object[] into a String[] without problems. So it makes some sense that the arrays returned from the two toArray() methods behave differently if they were created differently.

I'm still a little weirded out by this whole thing though, I just assumed the direct cast would work and the alternative for converting Object[] to String[] (create a new array, make a loop over all elements, cast each element in turn and copy to second array) sounds ugly and expensive.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Henry Wong:

I guess the *ideal* solution would have been to traverse the collection, looking for the base common object, and create an array of that common object. But they didn't do that.


They couldn't do that for two reasons:

1) The type needs to be known at compile time, when the content of the collection is unknown.

2) Even if the collection only contains Strings, you could later want to replace one of those Strings in the array with, say, a Number.


The soul is dyed the color of its thoughts. Think only on those things that are in line with your principles and can bear the light of day. The content of your character is your choice. Day by day, what you do is who you become. Your integrity is your destiny - it is the light that guides your way. - Heraclitus
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Daniel Bauer:

Why *can't* you just cast an Object[] into a String[] if all of the elements in the array are actually Strings? The reverse cast (from String[] to Object[]) works as expected. Let's say there is no Collection involved, you already received an Object[] from somewhere and wanted to pass it on to another method which accepts String[]. Is the only way to do this to create a new array, loop over the elements and copy and cast each one in turn? Sounds a bit mad.


Note that a cast doesn't change the object. So if you passed an Object[] to a method that expected a String[], the array couldn't be converted to a String[] at all, because an object can't change its type.

Also remember that Java is multi-threaded. So if the array only contains Strings at the time it is passed to the method, another thread could still put other objects into it while the method that thinks it is working on a String[] is accessing it.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Having thought a little bit more about it...

Originally posted by Ilja Preuss:
1) The type needs to be known at compile time, when the content of the collection is unknown.


That's, of course, bogus. Simply forget about it...


2) Even if the collection only contains Strings, you could later want to replace one of those Strings in the array with, say, a Number.


This issue actually is even more subtle:



If numberCollection contained Integers and Doubles, the above would work.

If numberCollection contained only Integers, it would throw an ArrayStoreException when trying to assign the Double.

If numberCollection contained Integers and Longs, the above would work *with current JREs*. If Sun decided to introduce a new base class for Integer and Long, say WholeNumber, it suddenly would fail.

It's not clear to me what toArray should return for an empty collection.

And in the presence of multiple interface inheritance, the type of the array to return could in fact become ambiguous.
Jim Yingst
Wanderer
Sheriff

Joined: Jan 30, 2000
Posts: 18671
Um... toArray() (with no args) returns an Object[], period. Casting it to Number[] will throw a ClassCastException, regardless of what's inside.
[ January 11, 2007: Message edited by: Jim Yingst ]

"I'm not back." - Bill Harding, Twister
Daniel Bauer
Ranch Hand

Joined: Apr 15, 2006
Posts: 49
I'm going to have to think aloud (and repeat back to you what you said) to make sure I've got it.

So if the array only contains Strings at the time it is passed to the method, another thread could still put other objects into it while the method that thinks it is working on a String[] is accessing it
Aha, that's an interesting point. And it doesn't even have to be threaded- what if I had:I guess there's nothing reasonable that colourStrings[0] could then be...

Note that a cast doesn't change the object.
I like that point.

So if I create the array with then I can't cast it to String[] even if there are Strings (currently) inside it. If I instead create it with then I can perform the cast fine. And if then I were to put non-Strings into the array (which the compiler thinks is OK) then I'd get a runtime ArrayStoreException.

I think I get it. But at first glance it seems so similar to casting Objects that I was stumped when it didn't work.

Thanks for all the replies!
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Jim Yingst:
Um... toArray() (with no args) returns an Object[], period. Casting it to Number[] will throw a ClassCastException, regardless of what's inside.


I was responding to the proposed alternative implementation by Henry:

I guess the *ideal* solution would have been to traverse the collection, looking for the base common object, and create an array of that common object.


I simply wanted to point out that such a solution would just open a can of worms.

Sorry for not making that more clear.
Jim Yingst
Wanderer
Sheriff

Joined: Jan 30, 2000
Posts: 18671
OK, I was responding to this line of code, which seems to originate in your post:

This line will compile but not run, regardless of what you put into numberCollection.

As for Henry's alternative - well I'm not sure it's "ideal" by any means. Aside from being more time-consuming to check all the types before creating the array and copying values, it seems like this would create more situations where we would have no way of knowing at compile time what the actual type of the array was. Seems like a mixed benefit at best. I guess the point is moot though since that's not the route Sun chose. Java arrays seem to have their share of warts either way.
Henry Wong
author
Sheriff

Joined: Sep 28, 2004
Posts: 18988
    
  40

As for Henry's alternative - well I'm not sure it's "ideal" by any means. Aside from being more time-consuming to check all the types before creating the array and copying values, it seems like this would create more situations where we would have no way of knowing at compile time what the actual type of the array was.


I was only partially serious... but... hmmmm....



Okay... There is no way to remove the extra iteration. And this doesn't take care of interfaces.... Maybe *ideal* is the wrong word.

Henry
[ January 11, 2007: Message edited by: Henry Wong ]
Ådne Brunborg
Ranch Hand

Joined: Aug 05, 2005
Posts: 208
For String[], there is a quick-and-dirty solution of calling the toString() method on each object in the Object[]:



would return an array containing the string representation of the objects - which, for a String object, is itself.

Then, if you want to be creative, and use this on any of your custom object, you could design a constructor or a fromString method re-creating the object from its String representation.


Entia non sunt multiplicanda praeter necessitatem
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Jim Yingst:
OK, I was responding to this line of code, which seems to originate in your post:

This line will compile but not run, regardless of what you put into numberCollection.


I wasn't trying to describe what happens with the current implementation, but what *would* happen with Henry's implementation.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Henry Wong:


I was only partially serious... but... hmmmm....



Okay... There is no way to remove the extra iteration. And this doesn't take care of interfaces.... Maybe *ideal* is the wrong word.


Returning null instead of an empty array for an empty collection would trouble me, too.

And all the other scenarios mentioned above, of course.
Henry Wong
author
Sheriff

Joined: Sep 28, 2004
Posts: 18988
    
  40

Returning null instead of an empty array for an empty collection would trouble me, too.

And all the other scenarios mentioned above, of course.


Can't fixed the other scenarios... but I can fix this one.




Have a great weekend,
Henry
 
 
subject: Casting Object[] into String[]