Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java array narrow casting rules

According to JLS 7, 5.1.6 Narrowing Reference Conversion

• From any array type SC[] to any array type TC[], provided that SC and TC are reference types and there is a narrowing reference conversion from SC to TC.

Object[] objArr = {"a","b","c"};
String[] strArr = (String[])objArr; // ClassCastException

in the above example, both objArr and strArr are reference types and there's a narrow reference conversion from Object to String.

Object obj = "a";
String str = (String)obj;

but the following codes works fine:

Object[] objArr = (Object[])java.lang.reflect.Array.newInstance(String.class, 3);
String[] strArr = (String[])objArr;

I want to ask the rules java uses to do the casting. As far as I can tell the differences between the two examples is that, in the first one, objArr is an Object array with component of type Object. the second is an Object array with component of type String.

Please note that, I am not asking HOW to do the conversion, don't show me how to do this using Arrays.copyOf or other libraries.

like image 674
Sawyer Avatar asked Oct 02 '12 15:10

Sawyer


2 Answers

Object[] objArr = {"a","b","c"};
String[] strArr = (String[])objArr;

With that the problem is {"a","b","c"} which is an array of Objects not Strings.

It's like doing something as follows -

Object obj = new Object();
String str = (String) obj; //ClassCastException

No exception with the following -

Object[] objArr = new String[] {"a","b","c"}; //Which is the case when you are using reflection
String[] strArr = (String[])objArr; //No exception

And it's like doing something as follows -

Object obj = new String();
String str = (String) obj;
like image 118
Bhesh Gurung Avatar answered Nov 16 '22 00:11

Bhesh Gurung


"objArr" in first example is of type Object[]. "objArr" in second example is of type String[].

While String extends Object (String is a type of Object) String[] does not extend Object[] (a String-Array Object is not a type of Object-Array)

The fact that in the first example you put Strings into an Object[] (Object-Array) object doesn't make it a String[] (String-array):

Object[] objArr = {"a","b","c"};
System.out.println(objArr instanceof Object[]);//true
System.out.println(objArr instanceof String[]);//false

String[] objArr2 = {"a", "b", "c"};
System.out.println(objArr2 instanceof Object[]);//false
System.out.println(objArr2 instanceof String[]);//true

== On Covariance

It's not something that I grasp completly, but here's what I get from it:

In Java arrays are covariant, generics are not. Meaning that this works:

Object[] o = new String[3];

and this doesn't:

//ArrayList<Object> o = new ArrayList<String>(); //compile time error

also, this works:

function1(new String[2]);
static void function1(Object[] o) {

    }

and this does not:

//function2(new ArrayList<String>()); //compile time error
 static void function2(ArrayList<Object> o) {

    }

There's a good reason why generics were restricted like that, but that's besides the point in this discussion.

However I believe that this is not really a discussion on covariance, but simply on how some classes are implemented in Java. An object of type String[] is also an object of type Object[] (with all the bad stuff that that brings), thus it is normal to be able to write:

Object[] o = new String[3];

however, BECAUSE of arrays' covariance, since you can treat a String[] as an Object[] and also in Object[] you can put whatever kind of types, you can specifically put Strings in an object array. I.E you can have this:

Object[] o = //whatever kind of array;
o[0] = //whatever kind of instance;

This however does not change the fact that o in the last example is of type Object[], therefore you cannot cast it to a String[].

like image 1
Shivan Dragon Avatar answered Nov 15 '22 23:11

Shivan Dragon