This Tutorial Explains How to Simulate the Functionality of Generic Array in Java using Object Array and also using Reflection Class with Simple Example:
We have already discussed Java generics in one of our previous tutorials. Java allows generic classes, methods, etc. that can be declared independent of types. However, Java does not allow the array to be generic.
The reason for this is that in Java, arrays contain information related to their components and this information is used to allocate memory at runtime. When generics are used, due to type erasure, the byte code does not contain any generics information.
=> Visit Here To Learn Java From Scratch.
Table of Contents:
Generic Array In Java
If you have defined a generic array, then the component type will not be known at runtime. Thus it is not advisable to define arrays as generic in Java.
A Generic Array definition is as shown below:
E [] newArray = new E[length];
The compiler does not know the exact type that is to be instantiated as the type information is not available at runtime.
So instead of arrays, whenever generics are required, you should prefer the list component of the Java Collections framework. However, you can create generic structures that are array-like using object array and reflection feature of Java.
These two approaches that allow us to define arrays of different data types are explained below in detail.
Create And Initialize The Generic Array
In this section, let’s create an array-like structure that is generic in nature. Using these structures, you will be able to create arrays by providing the data type as an argument.
Using Object Array
This approach uses the array of type Objects as a member of the main array class. We also use get/set methods to read and set the array elements. Then, we instantiate the main array class that allows us to provide the data type as required.
This simulates the generic array.
The following program demonstrates the use of object array to create a Generic array-like structure.
import java.util.Arrays; class Array<E> { private final Object[] obj_array; //object array public final int length; // class constructor public Array(int length) { // instantiate a new Object array of specified length obj_array = new Object [length]; this.length = length; } // get obj_array[i] E get(int i) { @SuppressWarnings("unchecked") final E e = (E)obj_array[i]; return e; } // set e at obj_array[i] void set(int i, E e) { obj_array[i] = e; } @Override public String toString() { return Arrays.toString(obj_array); } } class Main { public static void main(String[] args){ final int length = 5; // creating integer array Array<Integer>int_Array = new Array(length); System.out.print("Generic Array <Integer>:" + " "); for (int i = 0; i < length; i++) int_Array.set(i, i * 2); System.out.println(int_Array); // creating string array Array<String>str_Array = new Array(length); System.out.print("Generic Array <String>:" + " "); for (int i = 0; i < length; i++) str_Array.set(i, String.valueOf((char)(i + 97))); System.out.println(str_Array); } }
Output:
In the above program, we have defined a class Array that is generic. The object array is a member of the class that is instantiated using a constructor and length. We also use the generic get and set methods that are used to read and set an array element of a particular type.
Then we create instances of this array class. While creating instances, we can specify the desired type. In the above program, we have created two arrays of type Integer and String and then we populate these arrays with appropriate values (using the set method).
Finally using the overridden ‘toString’ method we display the contents of each of these instances.
Using Reflection
In this approach, we use a reflection class to create a generic array whose type will be known only at runtime.
The approach is similar to the previous one with just one difference i.e. we use reflection class in the constructor itself to instantiate an object array by explicitly passing the data type information to the class constructor.
This type of information is passed to the Array.newInstance method of reflection.
The following program shows the usage of reflection to create a generic array. Note that the entire program structure is similar to the previous approach with just the difference in the usage of reflection features.
importjava.util.Arrays; class Array<E> { private final E[] objArray; public final int length; // class constructor public Array(Class<E>dataType, int length){ // create a new array with the specified data type and length at runtime using reflection this.objArray = (E[]) java.lang.reflect.Array.newInstance(dataType, length); this.length = length; } // get element at objArray[i] Eget(int i) { returnobjArray[i]; } // assign e to objArray[i] void set(int i, E e) { objArray[i] = e; } @Override public String toString() { return Arrays.toString(objArray); } } class Main { public static void main(String[] args){ final int length = 5; // create array with Integer as data type Array<Integer>int_Array = new Array(Integer.class, length); System.out.print("Generic Array<Integer>:" + " "); for (int i = 0; i < length; i++) int_Array.set(i, i + 10); System.out.println(int_Array); // create an array with String as data type Array<String>str_Array = new Array(String.class, length); System.out.print("Generic Array<String>:" + " "); for (int i = 0; i < length; i++) str_Array.set(i, String.valueOf((char)(i + 65))); System.out.println(str_Array); } }
Output:
The above program shows arrays of two types i.e. Integer and String created from the Arrays generic class.
Generic Array Creation Error
We have already discussed the implications of creating generic arrays in Java and why it is not possible to have generic arrays in Java. Another explanation to this is that arrays in Java are covariant whereas generics are not. Generics are invariant.
By covariance, we mean that an array of the subtype can be assigned to its supertype reference.
This means the following statement will work fine.
Number numArray[] = new Integer[10];
As Integer is a subtype of Number, the above statement compiles fine.
But if we use the same concept with generics, it will not work i.e. with generics, we cannot assign subtype generic to a supertype generic.
The statement, List<Number>objList = new ArrayList<Integer>(); will give a compilation error as generics are not covariant like arrays.
Keeping the above reason in mind, we cannot have something like below too:
public static ArrayList<object1>[] myarray = new ArrayList<object1>[2];
This statement will fail to compile with the error, “generic array creation” as we cannot declare an array of references to a specific generic type.
We can, however, create an array of references to a specific generic type using wildcard <?>. The above statement can be compiled successfully with a slight change of using a wildcard as shown below.
public static ArrayList<?>myarray = new ArrayList<?>[5];
The above statement will compile successfully.
The following program shows a demonstration of using wildcards.
import java.util.*; //generic array class classArr<T> { T tarray[]; Arr(T myarray[]) { tarray = myarray; } @Override public String toString() { return Arrays.toString(tarray); } } public class Main { public static void main(String[] args) { // Arr<Integer>tarray[] = new Arr<Integer>[5]; //error: generic array creation //initialize new array objects Arr<Integer> arr1 = new Arr<Integer>(new Integer[]{2,4,6,8,10}); System.out.print("Array with Integer type:" + " "); System.out.println(arr1); Arr<String> arr2 = new Arr<String>(new String[]{"aa", "bb", "cc", "dd"}); System.out.print("Array with String type:" + " "); System.out.println(arr2); //define array objects using wildcard Arr<?>arr3[] = new Arr<?>[5]; arr3[0] = new Arr<Integer>(new Integer[]{10, 20, 30, 40, 50}); System.out.println("Integer array: " + arr3[0]); arr3[1] = new Arr<Float>(new Float[]{1.1f, 2.2f, 3.3f, 4.4f, 5.5f}); System.out.println("Float array: " + arr3[1]); } }
Output:
In the above program, we have the first statement in the main method that indicates the invariance of generics. This statement will flash the compilation error (shown in comments). The next array creation is as per the rules of generics and thus they compile successfully.
Frequently Asked Questions
Q #1) What is a Generic Array?
Answer: Arrays that are independent of the data type and whose type of information is evaluated at runtime are Generic arrays. Generics are similar to templates in C++.
Q #2) Can you create a Generic Array in Java?
Answer: Arrays are covariant in Java i.e. any subclass array can be assigned to a supertype array. Generics, however, are invariant i.e. you cannot assign subclass type array to superclass type.
Secondly, the generics information is removed from JVM and thus, the array whose memory allocation is done at runtime does not know which type is to be assigned to the array. Thus, arrays and generics do not go well together in Java.
Q #3) What is Type E in Java?
Answer: <E> acts as a placeholder for generics and represents any type of element.
Q #4) What is Type Erasure in Java?
Answer: A process carried out by Java compiler by which the parameterized types used in generics are removed and mapped to raw types in byte code. As such, the byte code does not contain any information on generics.
Q #5) What is a Raw Type in Java?
Answer: Raw types are generic types without using the type parameter. E.g. List is a raw type; whereas List<Integer> is a parameterized type.
Conclusion
In Java, the generic array cannot be defined directly i.e. you cannot have a parameterized type assigned to an array reference. However, using object arrays and reflection features, you can simulate the generic array creation.
We have seen these two approaches in this tutorial along with the details of generic array creation error and the possibilities to prevent such error. In a nutshell, in Java, you can say arrays and generics don’t go hand in hand as arrays are covariant while generics are invariant.
=> Check Out The Perfect Java Training Guide Here.