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];
Related
consider the code below:
ArrayList<Double> list1 = new ArrayList<>();
list1.add(1.5);
list1.add(2.5);
list1.add(3.5);
ArrayList<Double> list2 = (ArrayList<Double>)list1.clone();
Date[] list3 = {new Date(), new Date(4664316)};
Date[] list4 = list3.clone();
int[] list5 = {1, 2};
int[] list6 = list5.clone();
why list.clone() requires casting, while list3.clone() and list5.clone() don't need casting? I know the difference is between array and ArrayList, but not sure exactly why.
Please read the documentation.
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.
But in ArrayList, an Object is returned, so a cast is needed.
Clone() method signature returns an object type.
By convention, the object returned by this method should be independent of this object (which is being cloned)
Hence the cast is required.
Arrays behave differently and return a correctly typed clone array. So no casting required.
In java, the two data types are reference types and primitive types. Reference types are references to objects while primitive types directly contain values.
now, the new keyword in java is used to create the instance of an Object, for example,
String s3 = new String("foo")
or
String[] myStringArray = new String[3];
This is ok because String is a class and not a primitive data type.
but since boolean or int are primitive data types, why do we use new keyword with them when creating an array,
int[] myIntArray = new int[3];
int[] myIntArray = new int[]{1,2,3};
boolean[] array = new boolean[size];
Shouldn't new keyword be only use with reference data types ?
Because when you do int[] myIntArray = new int[3]; you are creating an array.. And an array is also an Object.
public static void main(String[] args) {
int[] arr = new int[5];
System.out.println(arr instanceof Object);
}
O/P :
true
EDIT :
If you look at the byte code, you will have this call for new int[5]
1: newarray int and newarray is defined here.
So, basically the compiler is hiding the truth from you :P
The new keyword refers to the array you create. In fact,
In the Java programming language, arrays are objects, are dynamically
created, and may be assigned to variables of type Object. All methods
of class Object may be invoked on an array.
Check this for more details
array is reference type therefor you need to use new operator to create it.
Arrays are objects that can be assigned to a variable of type Object
I am trying to make a generic function into which I pass an array (int[], String[]) and gets back the sorted version of it using TreeSort. I haven't even had the chance to begin to debug it since the mainclass refuses to accept an int[] array as the parameter.
TreeSort
public T[] treeSort(T[] arr) {
T[] result = (T[]) new Object[arr.length];
for (int i = 0; i < arr.length; i++)
if (arr[i] != null)
add(arr[i]);
Iterator it = iterator();
int count = 0;
while (it.hasNext())
result[count++] = (T) it.next();
return (T[]) result;
}
Main
BinarySearchTree<Integer> tree2 = new BinarySearchTree<>();
int[] unsortedNums = {6,3,7,4,1,2,9,8,5};
int[] sortedNums = tree2.treeSort(unsortedNums);
Error
The method treeSort(Integer[]) in the type BinarySearchTree is not applicable for the arguments (int[])
So I attempted to change unsortedNums and sortedNums from int to Integer (why should this matter?), and now running it yields this error:
Errorr 2
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable;
on line
T[] result = (T[]) new Object[arr.length];
In a generic method in Java, it is not allowed to create an array of the generic type. You are trying to circumvent this rule by creating an Object array and casting it to the generic type, but the compiler is too smart and prevents this. The reason for this rule is that the generic type could well be an Interface, so the runtime wouldn't know which class to create.
For your example, if you really need to keep the unsorted array as it is and return a copy (usually, sorting happens in-place so you would just edit the original array), you can either let the user pass the type of the generic (as in sp00m's answer), or let the user supply the destination array as a parameter so you don't need to create an array yourself.
IMO, the best practice here would be to make your sorting happen in place instead of returning a sorted copy of the data.
Instead of:
T[] result = (T[]) new Object[arr.length];
You have to use:
T[] result = (T[]) Array.newInstance(clazz, arr.length);
Where clazz is the type of the nested elements:
public T[] treeSort(T[] arr, Class<T> clazz) {...}
Finally, when calling, use the boxed primitive:
Integer[] unsortedNums = { 6, 3, 7, 4, 1, 2, 9, 8, 5 };
Integer[] sortedNums = treeSort(unsortedNums, Integer.class);
In Java, arrays know their component types at runtime (the component type is part of the actual runtime array type), and thus you need to specify the component type at runtime to create an array of that type.
#spoom's answer showed you how to create the array given a class object passed into the function. However, you actually already have that information without passing it separately. The parameter arr is of type T[], which means the array object's actual runtime type must be T[] or a subclass thereof. We can extract the component type from that and use it to create the new array:
T[] result = (T[]) Array.newInstance(arr.getClass().getComponentType(), arr.length);
This is how, for example, Arrays.copyOf() and related functions work.
You can't use primitive types (int) in generitcs. Try to use object wrapper, for example
Integer[] unsortedNums = {6,3,7,4,1,2,9,8,5};
Integer[] sortedNums = tree2.treeSort(unsortedNums);
Combining Generics and arrays is problematic in Java; check out the article Restrictions on Generics for details.
As to solving the problem, the easiest thing to do would be not using an array but rather a List. So for example:
public List<T> treeSort(List<T> list) {
List<T> result = new ArrayList<T>(list.size());
for (int i = 0; i < list.size(); i++)
if (list.get(i) != null)
result.add(list.get(i));
Iterator<T> it = list.iterator();
int count = 0;
while (it.hasNext())
result.set(count++, it.next());
return result;
}
Mind you, I haven't tested the logic.
When I do the following,
arrayList1 - contains one element and it is an int[].
arrayList2 - not compiling (Error : The constructor ArrayList<Integer>(List<int[]>) is undefined)
arrayList3 - contains 7 elements and they are Integer objects
Here's the code:
int[] intArray = new int[]{2,3,4,5,6,7,8};
ArrayList arrayList1 = new ArrayList(Arrays.asList(intArray));
ArrayList<Integer> arrayList2 = new ArrayList<Integer>(Arrays.asList(intArray));
Integer[] integerArray = new Integer[]{2,3,4,5,6,7,8};
ArrayList<Integer> arrayList3 = new ArrayList<Integer>(Arrays.asList(integerArray));
Question :
Why doesn't the compiler auto-box the elements in the int[] to Integer and create an ArrayList<Integer>? What is the reason behind this? Is that my stupidity or some other reason?
The difference is int[] is itself an Object, whereas Integer[] is an array of references to Integer object.
Arrays.asList(T...) method takes variable arguments of some type T with no upper bounds. The erasure of that method is Arrays.asList(Object...). That means it will take variable number of arguments of any type that extends from Object.
Since int is not an Object, but a primitive type, so it can't be passed as individual element of T[], whereas int[] is an Object itself, it will go as first element of the T[] array (T... internally is a T[] only). However, Integer[] will be passed as T[], with each reference in Integer[] passed as different argument to T[].
And even if you would argue that compiler should have done the conversion from each element of int[] array to Integer, well that would be too much work for the compiler. First it would need to take each array element, and box it to Integer, then it would need to internally create an Integer[] from those elements. That is really too much. It already has a direct conversion from int[] to Object, which it follows. Although I have always wished Java allowed implicit conversion from int[] to Integer[], that would have made life simpler while working with generics, but again, that's how the language is designed.
Take a simple example:
Object[] array = new Integer[10]; // this is valid conversion
Object[] array2 = new int[10]; // this is not
Object obj = new int[10]; // this is again a valid conversion
So, in your code Arrays.asList(intArray) returns a ArrayList<int[]> and not ArrayList<Integer>. You can't pass it to the ArrayList<Integer>() constructor.
Related:
int[] and Integer[]: What is the difference?
An int[] is not the same as an Integer[].
An array has as associated Class object. The class object for an array of primitive ints is [I. The class object for an array of Integer is [Ljava/lang/Integer.
An array is itself an object, so converting between two objects of the same type is an identity conversion. Converting between two different typed objects isn't - and int[] and Integer[] are definitely different, as evidenced by the bytecode above.
Lastly, bear in mind that autoboxing would only really apply if there was an associated boxing conversion.
Technically it is possible to do it of course. However autoboxing/unboxing of primitive type array to wrapper type array is more than what you expect.
First look into the auto-boxing/unboxing of Java: What it does is simply a syntax sugar to save you typing the primitive wrapper code. e.g.
Integer i = 10;
Compiler knows that it is expecting an Integer, but int present instead. Therefore what the compiler doing is translating your code to:
Integer i = Integer.valueOf(10);
It does similar thing for unboxing: when in situation that it expects int but Integer is present, compiler replace it with varName.intValue()
Back to array. There are two problems we can forsee:
The first problem is, there is no straight-forward way to transform from an int array to an Integer array. You may argue that the compiler can transform
int[] intArray = ....;
Integer[] wrapperArray = intArray ;
to
Integer[] wrapperArray = new Integer[intArray.size()];
for (int i = 0; i < intArray.size(); i++) {
wrapperArray[i] = Integer.valueOf(intArray[i]);
}
but that seems too much for a syntax sugar.
The second big problem is, when you are passing it as a parameter to a method, if autoboxing/unboxing happens for array, instead of reference of original array is passed, you are now passing the reference of a copy of the original array. In case you are changing the content of array in your method, the original array will not be affected. That can bring you lots of surprises.
e.g.
void foo(Integer[] arr) {
arr[0] = 0;
}
// invoking foo in some code:
int[] intArr = new int[]{9,8,7,6};
foo(intArr);
// intArr[0] will still be 9, instead of 0
Because int[] and Integer[] both are objects. First will hold primitive int values, which are not of type Object while second will store references of Integer objects, which are of type Object.
arrayList1 is really a List of size one.
http://ideone.com/w0b1vY
arrayList1.size() = 1
arrayList3.size() = 7
The int[] is being cast to a single Object. That Object cannot be cast to Integer.
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.