why can't cast Object[] to String[] - java

No error
Object[] a = new String[]{"12","34","56"};
String[] b = (String[]) a;
No error
Object a = new String[]{"12","34","56"};
String[] b = (String[]) a;
Run time error : ClassCastException
Object[] a = new Object[3];
a[0] = "12";
a[1] = "34";
a[2] = "56";
String[] b = (String[]) a;
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?

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.

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.

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];

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;

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.

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.

Related

Why it's wrong when the variable of Object[] as elements of Object[] transfrom to Object[][]

I want to use object[] as the element of object[] to transform it to Object[][], but it can't run. So I want to know why it is wrong. And how can I achieve that giving a number k, and create a array that is k dimensional.
Here is the wrong code:
public static void main(String[] args) {
Object[] s = new Object[3];
s[0] = new Integer[1];
s[1] = new Integer[1];
s[2] = new Integer[1];
Object m = s;
Integer[][] t = (Integer[][])m;
System.out.println(Arrays.toString(t));
}
The type Object[][] is saying that the array can only contain other Object[] instances.
But an Object[] can also contain things that are not arrays of Object.
Note that when you cast from one reference type to another, no value conversion occurs, and the actual type of the object doesn't change. All that happens is that the runtime system checks that the object's actual runtime type is assignment compatible with the type you are casting to.
To illustrate why it has to be like this, consider the following:
Object[] s = new Object[2];
s[0] = new Integer[1];
Integer[][] t = (Integer[][]) s; // ONE
s[1] = "Hello";
Integer[] u = t[1]; // TWO
If (hypothetically!) the cast at "ONE" is allowed to succeed, then at "TWO" we would assign a String to a variable of type Integer[].
That cannot be allowed to happen. The cast must fail.
The second part of your question is this:
And how can I achieve that giving a number k, and create a array that is k dimensional.
It depends on what you mean:
You can create a custom class that behaves like a K-dimensional array, though you won't be able use Java array syntax to access and update cells.
You can construct a K-dimensional array out of Object[] objects, though you will need to do a lot of type-casting to use it. And under no circumstances will you be able to cast it to an array with a higher number of dimensions.
You can use java.lang.reflect.Array.newInstance(Class, int...) to create an array with K dimensions. The only problem that the declared type is Object so you still need a type cast:
Integer[][][] array = Array.newInstance(Integer.class, 3, 3, 3);
However, if you want K to be a parameter, then
int[] dimensions = new int[K];
for (int i = 0; i < K; i++) {
dimensions[i] = 2;
}
Object array = Array.newInstance(Integer.class, dimensions);
The problem is that when K is a parameter, you cannot declare a variable whose Java type is a K-dimensional array of Integer. If you want to subscript the array using [], you will need to resort to something like this:
switch (K) {
case 1:
Integer[] oneD = (Integer[]) array;
oneD[1] = 42;
break;
case 2:
Integer[][] twoD = (Integer[][]) array;
twoD[1][1] = 42;
break;
// etcetera
}
To answer the part
And how can I achieve that
You can do the below transform
Integer[][] t = Arrays.stream((Object[])m).map(Integer[].class::cast).toArray(Integer[][]::new);

Literally BUILD my own Multidemensional Jagged Array

Can anyone tell me what is wrong with this approach of creating an array, it seems still works :
Object [] a = new Object[5];
Object [] b = new Object[5];
Object [] c = new Object[5];
Object [] d = new Object[6];
Object [] e = new Object[5];
a[1] = b;
a[2] = c;
b[1] = d;
d[1] = e;
a[1][2] = c; // error, how do I make this work if I insist of building my array like this?
What exactly I just did? Is this technically a multidimensional Array? Why can't I use for example a[1][2][2] to access the element, how can I access my data in a similar fashion?
I assume you know that obj[] is actually a reference value, so, I am looking to "bring the object out of that reference value", so I can use the array operator[] to access the value. For example, I declare a 1D array, like what I have in my original question, so I can use a[1].[2] to acess the value store in the array stored in a[1]
Object [] a= new Object[5];
Object [] b= new Object[5];
a[1] = b;
So maybe a[1] then something that bring the OBJECT out of reference value in a[1], which is b[], then use [2] to bring out the value store at b[2].

int[] arr2 = arr1.clone() compiles without casting RHS (Object) to (int[]). Why derivedObj = baseObj allowed with no cast to Derived (i.e. int[])?

int[] is the type which is derived from java.lang.Object (subtype of it).
So it shall be prohibited to assign int[] array = new Object() (without casting) for same reason as I cannot write (without casting) derivedObj = baseObj (but I can write derivedObj = (Derived) baseObj).
Why the code below compiles (and works in runtime) fine? It shall give error that Object type returned by clone cannot be implicitly cast to subtype of Object (namely, int[]) without explicitly casting like int[] ar2 = (int[]) ar1.clone();, which also compiles and works fine.
int[] ar1 = { 1, 2, 3, 4, 5 };
//now we assign Base class to Derived class without explicit cast
int[] ar2 = ar1.clone(); // why no compile ERR here???
System.out.println(Arrays.toString(ar2)); // output: [1, 2, 3, 4, 5]
but code below would fail to compile, and I understand why: we cannot cast from Base class to Derived class without explicit cast (thinking of int[] as subclass of Object).
Object obj = new Object();
ar2 = obj; // c.ERR!! cannot convert from Object to int[]
MY GUESS IS LIKE THIS: clone() is overridden in primitive arrays to return not Object, but subclass of Object, so int[].clone() returns int[] !!! It is possible for overriden method to return subtype (more specialized type). Besides, this overridden clone() within int[] "class" ha also its visibility changed from protected to public (increasing visibility also allowed while overriding).
Proof of concept:
int[] arr = { 1, 2, 3 };
System.out.println((arr.clone()).getClass()); // class [I
System.out.println((arr.clone()).getClass().getCanonicalName()); //int[]
More experiments:
User-defined method with same signature as clone() within Object (see snippet below) gives compile error, unlike clone() in topmost snippet above. Also note, that in snippet below there is no difference, whether method returns int[] as "return arr" or "return (Object) arr"):
public class MyTest {
static Object returns_arr_as_Object() {
int[] arr = { 1, 2, 3 };
return arr; // snippet don't change if add cast: (Object) arr
}
public static void main(String[] args) {
Object obj = returns_arr_as_Object(); // fine!
int[] myarr;
myarr = returns_arr_as_Object(); // c.ERR! can't Object -> int[]
BUT IF WE CHANGE RETURN TYPE OF returns_arr_as_Object() FROM Object to int[], the snippet compiles fine!
Additional info:
§4.3.1 of Java Language Specification: "arrays are objects" + "The
supertype relation for array types is not the same as the superclass relation. The direct supertype of Integer[] is Number[] according to
§4.10.3, but the direct superclass of Integer[] is Object according to
the Class object for Integer[] (§10.8). This does not matter in
practice, because Object is also a superTYPE of all array types."
Arrays are covariant (unlike parametrized generics, which are invariant), meaning that Integer[] IS Object[] (but not vice versa, and seems like it doesn't apply to int[] vs Object[])
Additional experiments:
Snippet 1:
int[] ar1 = { 1, 2 };
int[] ar2 = { 10, 20 };
Object obj = ar2; // now compiler knows that obj points to int[]
ar1 = obj; // c.ERR: cannot convert from Object to int[]
Snippet 2:
int[] ar1 = { 1, 2 };
int[] ar2 = { 10, 20 };
Integer[] arInteger = { 10, 20 };
Object[] objArr = ar2; // c.ERR: can't from int[] to Object[]
Object obj = ar2; // COMPILES!, but useless: obj[0] is c.ERR
ar1 = obj; // c.ERR: cannot convert from Object to int[]
arInteger = obj; // c.ERR: cannot convert from Object to Integer[]
Object obj2 = arInteger;
ar1 = obj2; // c.ERR: cannot convert from Object to int[]
arInteger = obj2; // c.ERR: cannot convert from Object to Integer[]
Object[] obj2Arr = arInteger; // COMPILES FINE !!!
ar1 = obj2Arr; // c.ERR: can't convert from Object[] to int[]
arInteger = obj2Arr; // c.ERR: can't from Object[] to Integer[]
Object[] oArr = ar2; // c.ERR: cannot convert from int[] to Object[]
oArr = arInteger; // COMPILES!
System.out.println(oArr[0]); // output: 10
More links: cast primitive array to Object and back
P.S. My compiler is Eclipse (NEON 2).
The compiler knows that ar1.clone() is specifically an int[]. Thus it allows that assignment with no issue.
On the other hand, the compiler does not know whether or not obj is an int[]. It will thus not allow that assignment without an explicit cast (which is you telling the compiler that you know what you're doing and this is intentional.)
Oracle API docs for Object.clone() method explicitly states the following, which totally explains everything:
Note that all arrays are considered to implement the interface
Cloneable and that the return type of the clone method of an array
type T[] is T[] where T is any reference or primitive type.
It's because int[] isn't an object, it's a datastructure which you can think of as a data type. Int[] isn't a class therefore when you create an int[] you are not creating an object. You are creating a variable with the int[] datatype.

Cast object array to generic array

Currently, I am viewing the source code of java.util.ArrayList. Now I find the function public void ensureCapacity(int minCapacity) casts an object array to a generic array, just like code below:
E[] newData = (E[]) new Object[Math.max(current * 2, minCapacity)];
However, when I declare the array to a specific type, IDE will show an error.
Object[] arr = new Object[10];
int[] arr1 = (int[]) new Object[arr.length];
Any one is able to tell me the differences between them? Thanks a lot.
It's because E (in the source code of ArrayList) stands for some reference type, but not for some primitive type.
And that's why you get a compile-time error when trying to cast an array of Object instances to an array of primitives.
If you do (for example)
Object[] arr = new Object[10];
Integer[] arr1 = (Integer[]) new Object[arr.length];
the error will be gone.
You can never cast a reference type (anything that extends from Object) to a primitive type (int, long, boolean, char, etc.).
You can also not cast an array of a reference type like Object[] to an array of a primitive type like int[].
And primitives cannot stand in for a generic parameter.
int is not Object, but it's primitive.
Use Integer and it will work.
Object[] arr = new Object[10];
Integer[] arr1 = (Integer[]) new Object[arr.length];

This is a legal way to initialize an array. But how would I access its elements?

I am preparing for the entry-level Oracle certification - OCA - Java Programmer I, since they require people to take this one before taking the next one (used to be possible to just go for SCJP directly, which is equivalent of OCP - Java Programmer II)
I came across this question on array initialization, that got me a bit puzzled. Obviously, one can declare and initialize an array like this:
Object[] objects = { new Object[1], new Object[34] };
as the arrays are objects, you can stick object arrays into an object array. You can easily get at one or the other object array by doing objects[0] or objects[1] but where would you go from there? How would you access the, say, 16th Object from the object array stored under objects[1]?
Basically, my question can be simplified to this:
Object o = new Object[100];
The above compiles. However, how would one access individual objects in the Object array o?
An Object[] is also an Object, which is why your declaration
Object o = new Object[100];
works.* To access the elements, though, you need to cast it back to an Object[]. For example:
Object elt = ((Object[]) o)[3];
For your original declaration:
Object[] objects = { new Object[1], new Object[34] };
you will have to do a similar thing to access the 16th element of objects[1]:
Object elt = ((Object[]) (objects[1]))[15];
Of course, you can avoid all this casting by declaring:
Object[][] objects = { new Object[1], new Object[34] };
in the first place. Then you can just do objects[1][15].
* Note that this is true only of Object, which has special status as the root of the object hierarchy in Java. An Integer[] cannot be assigned to an Integer variable.
You'd have to cast it back to Object[]:
Object o = new Object[] {new String("abc"), null, new Integer(1)};
Object[] arr = (Object[]) o;
Object elem = arr[0];
System.out.println(elem);
This prints abc.
It works because System.out.println() is happy to take an Object. If it required a String, you'd need another downcast.

Categories