11
  1. No error

    Object[] a = new String[]{"12","34","56"};
    String[] b = (String[]) a;
    
  2. No error

    Object a = new String[]{"12","34","56"};    
    String[] b = (String[]) a;
    
  3. Run time error : ClassCastException

    Object[] a = new Object[3];
    a[0] = "12";
    a[1] = "34";
    a[2] = "56";
    String[] b = (String[]) a;
    
  4. Run time error : ClassCastException

    Object[] a = {"12","34","56"};    
    String[] b = (String[]) a;
    

Of course, we can downcast an Object[] variable back to String[] if it was created as an String[].

My question is why we can not cast Object[] to String[] when it was created as Object[] but all its members are String? Is it because of security reason or just not that useful to implement this?

zakinster
  • 10,508
  • 1
  • 41
  • 52
Don Li
  • 1,095
  • 2
  • 12
  • 17
  • Assylias provided a good answer from JLS' perspective.Alex explained why JLS does not allow this.So Alex's answer is what I'm after. Thanks all, guys. – Don Li Aug 09 '13 at 00:31

6 Answers6

8

Here's two reasons I can think of.

Firstly, if you change the original array, the casted array can become invalid. e.g.

 Object[] a = {"12","34","56"};   
 String[] b = (String[]) a; // pretend this is legal. a and b now point to the same array

 a[0] = new Object(); // clearly ok
 String x = b[0]; // No longer a string! Bad things will happen!

Secondly, the example you have chosen is very simple, but if you have a very large Object[] array and it's not clear to the compiler what is filling it, then it has no way of validating that every element of the array satisfies the cast.

Object[] a = new Object[10000];
// lots of weird and whacky code to fill the array with strings

String[] b= (String[]) a; // valid or no? The best-defined answer is to say no.
Alex MDC
  • 2,416
  • 1
  • 19
  • 17
5

It is defined in the JLS #5.5.3. In substance, a cast:

 r = new RC[]; TC[] t = (TC[]) r;

"works" at runtime iif RC is a subtype of TC (or TC itself). Whether RC actually only contains TCs is irrelevant and the compile-time type of r is not used either (what matters is the runtime type):

  • you can write: r = new String[]; Object[] t = (Object[]) r;, but
  • you can't write r = new Object[]; String[] t = (String[]) r;.

JLS extract:

If T is an array type TC[], that is, an array of components of type TC, then a run-time exception is thrown unless one of the following is true:

  • TC and RC are the same primitive type.
  • TC and RC are reference types and type RC can be cast to TC by a recursive application of these run-time rules for casting.

In your examples 3 and 4, RC = Object and TC = String and Object is not a subtype of String. In your examples 1 and 2, RC = String and TC = String so it works.

Note: the type in this context is the runtime type.

assylias
  • 321,522
  • 82
  • 660
  • 783
  • In JLS, it's TC[] = (TC[]) RC[]. So RC is Object and TC is Sting.And Object can be cast to String if it is Sting.So still I don't understand. – Don Li Aug 08 '13 at 07:25
  • The JLS does not explain why case 1 and 2 are allowed, which cast object[] to String[] – Don Li Aug 08 '13 at 07:27
  • And I have elaborated a little more. In your first example, you are not casting an `Object[]` to a `String[]`, you are casting a `String[]` (runtime type) to a `String[]`, which is allowed. – assylias Aug 08 '13 at 07:35
4

Because you are not casting individual member of array, you are casting the whole array instance which is of type Object[] and not String[].

Object[] a = new String[]{"12","34","56"};

Here the instance is of type String[] and the compile time type is Object[].

And in the next line you are casting it back to String[] which is allowed as the actual type or runtime type is String[].

But Object[] a = new Object[3]; here the actual type and Compile time type is Object[] and it is not String[]. So an Object[] cannot be String[].

Object[] a = new Object[1];
a[0] = "a"; //Actual type String 

So you can do this:

String aStr = (String)a[0];
Narendra Pathai
  • 41,187
  • 18
  • 82
  • 120
2

Array objects are not just a collection of their elements, they have classes just like other objects. The class for array of strings is a subclass of array of objects. That's why there's no error in your 1 or 2, but the last two are equivalent to

Object o = new Object();
String s = (String) o;
Joni
  • 108,737
  • 14
  • 143
  • 193
  • This is interesting.Can you show me what the name of class array and the class name of String[]?? – Don Li Aug 08 '13 at 06:55
  • 1
    @DonLi: They don't have a friendly name (other than `Object[]` or `String[]`). The internal name is something like `[Ljava.lang.Object;` or `[Ljava.lang.String;`. – cHao Aug 08 '13 at 07:00
  • 1
    The class name of string array is `[Ljava.lang.String;`. Since it's not a valid identifier you can't use it directly in a program, but you can use it with the reflection API for example. – Joni Aug 08 '13 at 07:01
  • 1
    you can define an array and do a `getClass()` on that array instance and print that and you will get that representation. – Narendra Pathai Aug 08 '13 at 07:04
  • 1
    Even simpler: the the class object literal `String[].class` – Joni Aug 08 '13 at 10:32
2

If all members Object array were Strings at the moment of casting you still could assign objects that are not Strings to the elements of this array later. So you would have String array with elements which are not Strings.

Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
0

This post provides a way to quickly create a String[] out of an Object[].

arrayOfUrls = imageUrls.toArray(new String[imageUrls.size()]);

Assuming of course that imageUrls is not null.

Community
  • 1
  • 1
craned
  • 2,991
  • 2
  • 34
  • 38