Generic array creation java error

I don't understand the error of Generic Array Creation. First I tried the following: public PCB[] getAll() { PCB[] res = new PCB[list.size()]; for (int i = 0; i < res.length...

I don’t understand the error of Generic Array Creation.
First I tried the following:

  public PCB[] getAll() {
       PCB[] res = new PCB[list.size()];
           for (int i = 0; i < res.length; i++) {
               res[i] = list.get(i);
            }
       list.clear();
       return res;
}

Then I tried doing this:

PCB[] res = new PCB[100];

I must be missing something cause that seems right. I tried looking it up I really did. And nothing is clicking.

My question is: What can I do to fix this?

the error is :

.Queue.java:26: generic array creation
PCB[] res = new PCB[200];
            ^
Note: U:Senior YearCS451- file      
uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error

Tool completed with exit code 1

asked Oct 5, 2010 at 17:00

Luron's user avatar

LuronLuron

1,1375 gold badges17 silver badges30 bronze badges

6

You can’t create arrays with a generic component type.

Create an array of an explicit type, like Object[], instead. You can then cast this to PCB[] if you want, but I don’t recommend it in most cases.

PCB[] res = (PCB[]) new Object[list.size()]; /* Not type-safe. */

If you want type safety, use a collection like java.util.List<PCB> instead of an array.

By the way, if list is already a java.util.List, you should use one of its toArray() methods, instead of duplicating them in your code. This doesn’t get your around the type-safety problem though.

answered Oct 5, 2010 at 17:43

erickson's user avatar

ericksonerickson

263k58 gold badges394 silver badges491 bronze badges

8

The following will give you an array of the type you want while preserving type safety.

PCB[] getAll(Class<PCB[]> arrayType) {  
    PCB[] res = arrayType.cast(java.lang.reflect.Array.newInstance(arrayType.getComponentType(), list.size()));  
    for (int i = 0; i < res.length; i++)  {  
        res[i] = list.get(i);  
    }  
    list.clear();  
    return res;  
}

How this works is explained in depth in my answer to the question that Kirk Woll linked as a duplicate.

answered Nov 19, 2010 at 8:35

gdejohn's user avatar

gdejohngdejohn

7,3711 gold badge33 silver badges49 bronze badges

Besides the way suggested in the «possible duplicate», the other main way of getting around this problem is for the array itself (or at least a template of one) to be supplied by the caller, who will hopefully know the concrete type and can thus safely create the array.

This is the way methods like ArrayList.toArray(T[]) are implemented. I’d suggest you take a look at that method for inspiration. Better yet, you should probably be using that method anyway as others have noted.

answered Oct 5, 2010 at 18:45

Mark Peters's user avatar

Mark PetersMark Peters

79.6k17 gold badges158 silver badges189 bronze badges

  1. Demonstration of the generic array creation Error in Java
  2. Possible Causes for generic array creation Error in Java
  3. Possible Solutions to Eradicate generic array creation Error in Java

Resolve Generic Array Creation Error in Java

This tutorial illustrates the generic array creation error via code sample and highlights why we have this error while creating a generic array. This discussion will lead to the solution where we will learn how to create a generic array using the Object array and reflect feature.

Demonstration of the generic array creation Error in Java

Example Code (DemoGenArray.java Class):

import java.util.Arrays;

public class DemoGenArray<T> {

    private T[] genericArray;

    DemoGenArray(int size) {
        genericArray = new T[size];//<----This Line Has Generic Array Creation Error
    }

    public T get(int index) {
        return (T) genericArray[index];
    }

    public void set(int index, T item) {
        genericArray[index] = item;
    }

    @Override
    public String toString() {
        return Arrays.toString(genericArray);
    }
}

In this class, we try to create and initialize a generic array with the specified array size. Further, we add elements to the array and return the items individually from the given index.

The toString() returns the whole array at once.

The issue is that we have a generic array creation error at the line we are pointing out in the constructor. Let’s explore the possible causes for this error which will help us to move towards its solution.

Possible Causes for generic array creation Error in Java

We are trying to create a generic array in our code, which is impossible in Java. It is because Java consciously decides to explicitly stop these syntaxes from working.

Remember, arrays are covariant (we can assign sub-type to its super-type reference) in Java while generics are not.

private T[] genericArray;
genericArray = new T[size];

The above two lines of code are the same as given below. You can use any one of them.

private T[] genericArray = new T[size];

It happens due to the type-erasure that Java implements. It is a process carried out by the Java compiler.

It removes parameterized types in generics and maps them to raw types in the byte code.

Remember, the byte code doesn’t have any details on generics. Using the type T will be fine at compile-time but has no meaning at run-time.

In other ways, we can circumvent this restriction that are given below with code examples. Let’s learn each of them below.

Possible Solutions to Eradicate generic array creation Error in Java

As we already know, the compiler does not have any information regarding parameterized types at run-time. So, whenever we are required to use generics, it is good to prefer and use the list component of the Java Collections framework instead of using arrays.

However, we can still create array-like generic structures, but this solution depends on whether it is checked or unchecked. We use the Object array if it is unchecked.

In the case of checked, we can use Java’s reflect feature. Let’s see how each of them works.

Solution 1: Use Object Array

Example Code (DemoGenArray.java class):

import java.util.Arrays;

public class DemoGenArray<T> {

    private Object[] genericArray;

    DemoGenArray(int size) {
        genericArray = new Object[size];
    }


    public T get(int index) {
        return (T) genericArray[index];
    }

    public void set(int index, T item) {
        genericArray[index] = item;
    }

    @Override
    public String toString() {
        return Arrays.toString(genericArray);
    }
}

Example Code (GenArray.java class):

public class GenArray {
    public static void main(String[] args) {

        DemoGenArray<String> strArray = new DemoGenArray(3);
        strArray.set(0, "one");
        strArray.set(1, "two");
        strArray.set(2, "three");

        DemoGenArray<Integer> intArray = new DemoGenArray(3);
        intArray.set(0, 10);
        intArray.set(1, 20);
        intArray.set(2, 30);

        DemoGenArray<Double> doubleArray = new DemoGenArray(3);
        doubleArray.set(0, 15.0);
        doubleArray.set(1, 110.0);
        doubleArray.set(2, 10.0);

        System.out.println("Integer Array: " + intArray);
        System.out.println("String Array: " + strArray);
        System.out.println("Double Array: " + doubleArray);
    }
}

Output:

Integer Array: [10, 20, 30]
String Array: [one, two, three]
Double Array: [15.0, 110.0, 10.0]

Here, we use the Object array to simulate the generic array because the DemoGenArray class is unchecked (weak typing). We can use this approach if we know that no type checking would be performed on objects passed as the arguments.

Further, we use generic get() and set() methods to return value and set value respectively. The get() method uses an explicit cast to T where T acts like a placeholder for the generics and represents any value/element.

This works fine if we use set() and get() methods and do not let the user directly access the Object array. Coming to the GenArray class, which contains the main() method.

Here, we create the instance of the DemoGenArray class.

We pass the required type while instantiating the DemoGenArray class and populating them. After that, we use the toString() method to write the contents of all instances.

Solution 2: Use reflect Feature

Example Code (DemoGenArray.java class):

import java.lang.reflect.Array;
import java.util.Arrays;

public class DemoGenArray<T> {

    private T[] genericArray;

    DemoGenArray(Class<T> classType, int size) {
       genericArray = (T[]) Array.newInstance(classType, size);
    }


    public T get(int index) {
        return (T) genericArray[index];
    }

    public void set(int index, T item) {
        genericArray[index] = item;
    }

    @Override
    public String toString() {
        return Arrays.toString(genericArray);
    }
}

Example Code (GenArray.java class):

public class GenArray {

    public static void main(String[] args) {
        DemoGenArray<String> strArray = new DemoGenArray(String.class, 3);
        strArray.set(0, "one");
        strArray.set(1, "two");
        strArray.set(2, "three");

        DemoGenArray<Integer> intArray = new DemoGenArray(Integer.class, 3);
        intArray.set(0, 10);
        intArray.set(1, 20);
        intArray.set(2, 30);

        DemoGenArray<Double> doubleArray = new DemoGenArray(Double.class, 3);
        doubleArray.set(0, 15.0);
        doubleArray.set(1, 110.0);
        doubleArray.set(2, 10.0);

        System.out.println("Integer Array: " + intArray);
        System.out.println("String Array: " + strArray);
        System.out.println("Double Array: " + doubleArray);
    }
}

Output:

Integer Array: [10, 20, 30]
String Array: [one, two, three]
Double Array: [15.0, 110.0, 10.0]

Here, we are using the reflection class to create the generic array whose type will only be known at run-time. This solution is similar to Solution 1 except for two differences in the DemoGenArray class.

First, we have a constructor in the DemoGenArray class that accepts two arguments, type and size of the array. Second, we initialize the genericArray using Array.newInstance() method.

The Array.newInstance() method creates a new array using the given dimension (size of the array) and component type.

In this post, we feature a comprehensive article which explains the Problem with creating Generic Arrays. The Java programming language added generics in September of 2004 in the Java 5.0 “Tiger” release. The system of generics or type parameterization, extends Java’s existing type system while providing type safety.

1. Introduction

Java has the Collections Framework, providing a library of generic data structures for use in Java software
development. The Collections Framework lacks on one data structure—an array. Yet the Java Collections
Framework has a type parameterized ArrayList.java and Vector.java data structures. Both data
structures use a dynamic array of one-dimension that utilizes an underlying array of java.lang.Object.

Java provides a built-in array that is an object that is included in the language specification since Java 1.0
from 1995. As an object, the built-in array is declared and instantiated in the Java code to a particulate type.
The built-in array is a container of objects that is a fixed number and length of possibly many dimensions.

Yet compile-time type safety using generics is not fully realized. Especially with the built-in array object.

2. Generic Arrays

The problem is when generics are used with the built-in array entity in Java. Consider the following Java class, TestArray1.java which declares two generic arrays around a single generic type parameter E. The Java class source code is:

class TestArray1 {
  public E[] array = new E[10];
}//end class TestArray1

The source code for TestArray1.java is declarative only, the arrays are not used. Two use cases of a generic
array are written: one for the generic array as a class attribute, and the other using a generic array in a static
(i.e., non-instance) method of the class.


2.1 Generic Error when Compiled

When compiled the following error is reported by the compiler:

Error: TestArray1.java.                                                             
      Line 3 At 22: generic array creation                                            
        public E[] array = new E[10];                                                 
                           ^             

There is one kind of error reported: generic array creation. This error corresponds directly to a use case of generic arrays.

Covariance

In Java, arrays are covariant, or use type specialization of general towards specific types, such as a Collection to a Set. However, generic type parameters are not covariant. Goetz explains, “The Collections classes use an ugly trick to get around this problem…” [Goet 2019]

Thus to use the built-in array with Java generics, or the generic type parameter E, the array must be of type java.lang.Object which is the great-great super-type in Java. Everything is an java.lang.Object, this is the ugly trick.

Object Array

Yet the drawback for using an Object array is the imperative for generics⎼to bind a data structure or variable to a specific type. A data structure of type Object can mix and match any type, and requires a type cast to convert to the original type. In this generics in Java are not useful⎼and this is the core problem.

The solution or answer to the problem is simple⎼a Java array class that is generic. Such a class is not in the Java collections framework, thus create it.

3. Java Array Class

The Java Array class is like the other data structures in the Java collections framework, it is a data structure. The implementation originally written is for simplicity, and does not implement any specific interfaces or extend any super-classes. The goal is to get a functional and useful array as a class Array.java to build upon. The skeleton of the generic type parameter Array.java class is:

class Array {
    Array(final int... dim);
    void init(final E elem);                                                             
    void init(final E[] elem);
    E get(final int...idx);
    void add(final E elem, final int... idx);					      	             
  }

Essentially you have a means to construct an array of any rank, and then any size for the dimensions

The array then has a method init() to initialize the array to a default or sentinel initial value. Lastly the array has the two primary methods, one to add() an element, and one to get() an element at a specific location within the array. The basic functionality of instantiate or create, initialize the overall array, and access an element.

3.1 Array Class Attributes

The Java array class has several attributes that define an instance of an Array.java class. The attributes of the generic type parameter Array.java class are:

 class Array {
    int size;                                                                             
    int dim[];                                                                            
    int rank;	                                                                             
    Object data[];                                                                    
    E elem;                                                                           
  }							

The Array.java class attributes are the data as a built-in array of Object, the boundaries of the array, such as size, rank, dimensions. Lastly, there is the elem, the initialization element.

3.2 Rank and Dimension with Varargs

The Java programming language added variable number or parameters or arguments, named Java variable arguments, or more simply varargs in Java 5.0 “Tiger” release in September 2004. This particular feature allows a constructor or method to take a varying number of parameters, and thus generalize parameters without duplicating a constructor or method simply for the number of parameters.

The generalization of the Array class uses a this specific Java feature: varargs, or variable arguments for creation and access of the elements in the Array.java class. This allows any rank (the number of dimensions) and also for each any number dimension for any non-negative integer.

Varargs also allows a rank to be general, so there is (at least theoretically…) no least upper bound on the rank of the Array.java class. Thus varargs allow for an overall generalization in defining and using a generic Array.java class.

Constructor using Varargs

The source code for the Array.java constructor illustrates using varargs for the dimensions to create a generic array object using the Array.java class. The rank and dimensions are generalized, so any rank of array is possible with any dimensional bounds. The source code for the Array.java constructor is:

  Array(final int... dims) {                                                           
    this.rank = dims.length;                                                           
    this.dim  = new int[rank];                                                         
    int size  = 1;                                                                     
  
    //compute size of 1-dim internal array                                             
    for (int x = 0; x < dims.length; x++) {                                             
      size = size * dims[x];                                                           
      dim[x] = dims[x];                                                                 
    }//end for                                                                                                                                                      
  
    //create internal "flat" array                                                                                                            
    this.data = new Object[size];                                                  
    this.size = size;                                                                   
  }//end constructor     
  
 

The varargs is the varying parameters passed as a primitive int as an array of integers in the variable dims. From dims the rank, dimensions for the boundaries of the array, and the overall internal “flat” array are calculated and created. Without the varargs, a constructor for each rank for different dimensions would be required. With a finite number of constructors for rank, the Array.java generic array class would be limited and not as general with varargs.

Accessor with Varargs

Accessing an element in the generic array is via the get and set methods. These are the access or accessor methods. Unlike the properties getter and setter methods, for the generic array class the dimension index specifies the element.

Using varargs, this generalizes the access methods for any rank or dimension to access an element. The dimensional index is checked against the boundaries and rank of the generic array for access.

Read Accessor Get

The getter method is the read accessor, it reads or copies a value from the array. The source code for the Array.java the get accessor method is:

  E get(final int... idx) {                                                            
    return (E) this.data[this.getIndex(idx)];                                      
  }//end get							  

Write Accessor Set

The setter method is the write accessor, it writes or copies a value into the array. The source code for the Array.java set accessor method is:

 
  void set(final E elem, final int... idx) {                                           
    this.data[this.getIndex(idx)] = elem;                                          
  }//end set								

For simplicity, the auxiliary method, getIndex() does the actual “heavy lifting” of computing the single dimension index, and checking the boundaries of the array index.

Auxiliary Methods to Help

Three auxiliary methods or helper methods do the actual processing in the generic Array.java class. One method, getIndex() computes a single index from the multiple index dimensions, and the two other methods isValidDim() and isValidIndex() validate that the index calculated, or given does not exceed the boundaries of the array. The interface source code for the auxiliary methods is:

  class Array {
    int getIndex(final int... idx);
    boolean isValidDim(final int... idx);						       
    boolean isValidIndex(final int idx);					      	             
  }//end class Array							

Heavy Lifting Getting an Element Index

The getIndex() method is the core function of the Array.java class. The getIndex() method calculates the index into the internal one-dimensional linear or “flat” array of an element. From compiler theory (and this alludes to the theory, but a more in-depth explanation is beyond the scope of explanation) an array is either indexed by row-major or column-major for an index. [Aho 2007]

For the Array.java class, it is immaterial, so long as the function is consistent for a given index for the dimension boundaries of the array instance. The source code for the getIndex() method is:

  int getIndex(final int... idx){                                                          
    isValidDims(idx);                                                                  
    int index = 0;                                                                     
    for(int x = 0; x < idx.length; x++) {                                             
      int i = idx[x];                                                                    
      for(int y = x + 1; y < idx.length; y++) {                                         
        i = i * dim[y];                                                                    
      }//end for                                                                                                                                                   
      index = index + i;                                                                 
    }//end for                                                                                                                                                        
    return index;                                                                          
  }//end getIndex			     			

The source code for the getIndex() method validates the dimensions of an index before actually calculating the one dimensional index into the internal linear array.

Validation of an Index

There are two methods to validate an index. One is to validate a multiple dimensional index, and the other is to validate a single index. The source code to validate a single dimensional index is:

 void isValidIndex(final int idx){                                                        
    if(idx = this.size) throw new RuntimeException("Index Overflow Error!");             
  }//end isValidIndex   

The isValidIndex() simply checks that the index is mathematically within the range of zero to the overall size of the internal array. If the index is not within the range, a runtime, unchecked exception is thrown. The source code to validate a multiple dimension index is:

  void isValidDims(final int... idx) {                                                   
    if(idx.length != this.dim.length) throw new RuntimeException("Rank Error");       
      for(int x = 0; x = dim[x]) throw new RuntimeException(“Index Overflow Error");         
        if(idx[x] < 0) throw new RuntimeException(“Index Underflow Error”);                            
      }//end for                                                                                                                                                    
  }//end isValidDims  

The isValidDims() simply traverses each dimension parameter, and checks that the rank for the index is the same as the rank parameter of the array. If the rank of the array and the index are not equal, a runtime, unchecked exception RuntimeException is thrown.

3.3 Other Non-Varargs Methods

The other methods are non-varargs, that either take no parameters as a getter accessor method, or take a single parameter. The two categories of methods are:

  1. Query the array parameters
  2. Unique array functionality

Query the Array

The query the array parameters accesses the parameters of the array, as a getter access method, or with a parameter. The array is queries for rank, overall size, the upper dimensions, and the dimension at a specific index within the rank. The interface for the query the array methods of the Array.java class are:

  class Array {
    int getDim(final int dim);
    int[] getDims();
    int getRank();
    int size();					      	                                   
  }//end class Array				

Unique Array Class Functionality

The unique array class functionality are a constructor and two methods that provide unique functionality. The two methods are to access and convert to a linear array of the Array class instance. The other functionality is a constructor that allows the instance of the Array class to be copied or replicated. The functionality for the Array.java class is:

  class Array {
    Array(final Array array);
    E getAt(final int idx);
    Object[] toArray();							      
  }		

Accessor as Linear Array

The getAt() method allows an array element to be accessed as if the Array class instance were a “flat” linear array of one dimension. The integer index is checked for validity, and the element is returned at a valid location with the internal one dimension array. The source code to access as a linear array is:

  E getAt(final int index) {                                                             
    this.isValidIndex(index); 				                                      
    return (E) this.data[index];                                                   
  }//end getAt  

Conversion to Linear Object Array

The toArray() method converts the Array class instance, or rather accesses the internal one dimensional array and returns it as an array of Object. The toArray() method returns a shallow copy of the internal linear array, not a deep copy. The getter accessor source code to access the linear Object array is:

  Object[] toArray() {                                                                    
    return this.data;                                                              
  }//end toArray 

Copy Constructor

The copy constructor allows an Array class instance to be duplicated but as a “deep” copy of an existing Array class instance. The array copied and the copy are of the same type parameter, dimensions, rank, and of course the elements. The source code of the copy constructor is:

  Array(final Array orig) {                                                              
    this.rank = orig.rank;                                                                       
    this.dim  = orig.dim;                                                                 
    this.size = orig.size;                                                               
    this.elem = (E) orig.elem;                                                    
    this.data = new Object[this.size];		                                    
    System.arraycopy(orig.data, 0, this.data, 0, this.size);                     
  }//end constructor copy  

The System.arraycopy() copies the original and creates a new Object array for the deep copy. The various Array class instance parameters are copied into the deep copy.

4. Using the Generic Array Class

Using the Java generic array Array.java is illustrated in source code by two examples of use:

  1. Bubble sort algorithm
  2. Multiplication table

Both examples illustrate by demonstration the use of the generic array class methods to create, initialize, access, query, and implement functionality around an array, but not using the built-in Java array. The source code illustrates and the output from the source code fragment is given.

4.1 Bubble Sort

The bubble sort is a basic, simple sorting algorithm, but is perfect to illustrate the use of the Array.java generic array class. The bubble sort is implemented using the generic Java array as:

 
  Array list = new Array(9).init(new Integer[]{3,5,7,4,8,0,2,1,6});  
  System.out.println(Arrays.toString(list.toArray()));                                  
        			        			                                                                                   
  boolean swapFlag = true;                                                               
  while(swapFlag) {                                                                       
    swapFlag = false;                                                                    
    for(int x=0;x 0) {                                     
        Integer temp = list.get(x);                                                       
        list.set( list.get(x+1), x);                                                      
        list.set( temp, (x+1));                                                         
        swapFlag = true;                                                                  
      }//end if                                                                                                                                                         
    }//end for                                                                                                                                                          
  }//end while                                                                                                                                                            
        			        			                                     
  System.out.println(Arrays.toString(list.toArray())); 

When run, the output from the bubble sort using the generic Java array is:

       			        			                                      
[3, 5, 7, 4, 8, 0, 2, 1, 6]                                                              
[0, 1, 2, 3, 4, 5, 6, 7, 8]                                                                
     

4.2 Multiplication Table

A basic, illustrative application of the Java generic array is to create a simple multiplication table of the integer from 1 to 10. This requires a two dimensional array of integers. After creating the multiplication table, the mathematical properties of identity and commutativity are verified. The source code for this use of the generic Java array is:

 

  Array multiplyTable = new Array(10,10).init(0);                    
  for(int x=0;x<multiplyTable.getDim(0);x++){                                            
    for(int y=0;y<multiplyTable.getDim(1);y++){                                            
      multiplyTable.set(x*y, x,y);		                                           
    }//end for                                                                                                                                                       
  }//end for                                                                                                                                                            
                                                                            		
  //check 1*n = n                                                                                                                                         
  for(int x=0;x<multiplyTable.getDim(0);x++){                                           
    if(multiplyTable.get(1,x) != x)                                                     
      throw new RuntimeException("Identity property doesn't hold!”);                     
    if(multiplyTable.get(x,1) != x)                                                    
      throw new RuntimeException("Identity property doesn't hold!”);                     
  }//end for   
                                                                      	 		        			                                                            
  //check m*n = n*m                                                                                                                                              
  for(int x=0;x<multiplyTable.getDim(0);x++){                                               
    for(int y=0;y<multiplyTable.getDim(1);y++){                                             
      if(multiplyTable.get(x,y) != multiplyTable.get(y,x) )                               
        throw new RuntimeException("Commutative property doesn't hold!");	                 
    }//end for                                                                                                                                                         
  }//end for

There is no output, because the identity and commutative mathematical properties for multiplication are true, and thus valid. But this illustrates the use of a two dimensional generic array. For other higher dimensions, other applications are possible.

5. Conclusion

The original problem demonstrated the problem with a type parameterized, or generic array using the built-in array entity. Using the same code fragment, but substituting the generic array Array.java, the source code is:

 
  class TestArray2 {                                                                                                                                                                                                                   
    public Array array = new Array(10);                                                                                                                                                                                              
  }//end class TestArray2 

When compiled there are no reported errors. The data structure Array.java achieves a solution to the original problem.

The type parameterization or generics in Java allows for type-safe classes, but there are trade-offs in the design of the generic type parameterization system. Thus generics in Java has some flaws relating to the built-in Java array entity. The Java Collections Framework unfortunately does not remedy the problem by providing an array data structure.

The solution is within the Java language and generic type parameterization. Simply design a generic Array class as a first-class object written in Java. On the surface, it seems a redundant, duplication of an existing Java entity—the array.

This is not replication; designing a Java Array class creates a generic, type-safe data structure that is a useful replacement for the built-in Java array entity. The trade-off is that without operator overloading, the syntax is less explicit about array access and operations, but is consistent with invoking methods on an object instance.

6. References

  • [Aho 2007] Aho, Alfred V., Lam, Monica S., Sethi, Ravi, and Ullman, Jeffrey D. Compilers: Principles, Techniques, and Tools, 2nd edition. Pearson Education, Inc., New York, New York, 2007, pp. 381 – 382.
  • [Goet 2019] Goetz, Brian. “Java theory and practice: Generics gotchas,” January 25, 2005. https://www.ibm.com/developerworks/java/library/j-jtp01255/index.html, Accessed September 28, 2019.

7. Download the Source Code

Introduction

I bet that most Java programmers fall into the trap of trying to create generic arrays at least once in their Java software development career, wondering why the compiler is complaining. Shouldn’t generics—introduced in Java 5—allow the use of type parameters everywhere? Why is List<T> genericList = new ArrayList<>() legal, but T[] genericArray = new T[10] is not? This blog post answers these questions and shows several fixes to solve this problem.

Arrays Versus Generics With Respect to Variance

The first principle one needs to know in order to understand the problem mentioned above is the fundamental difference between arrays and generics when it comes to variance. Variance, esp. the adjectives invariant and covariant (and also contravariant if one has a closer look at this topic), “refer[s] to how subtyping between more complex types relates to subtyping between their components”, according to Wikipedia. By “more complex types”, we speak of arrays and generic collections here. So an array, e.g., String[], is a complex type, whereas String is its component type. The same applies to generic collections, e.g., List<String>, where List is the complex type, and String is the type of its components.

The questions are:
If Super is a superclass of Sub (i.e., Sub is a subclass of Super), then …

  1. is Super[] also a superclass of Sub[]?
    (is Sub[] also a subclass of Super[]?)
  2. is List<Super> also a superclass of List<Sub>?
    (is List<Sub> also a subclass of List<Super>?)

In order to answer the question for arrays (question 1), please have a look at the following example program. Instead of the abstract types Super and Sub, I will use the concrete classes Number as the supertype and BigDecimal and BigInteger as the subtype for this and all upcoming examples in this blog post. Note that BigDecimal and BigInteger are “siblings”, so neither is a subtype of the other. They’re both “equally ranked” subtypes of Number.

import java.util.*;
import java.math.*;

public final class Variances {

    private void arrayVariance() {
        BigDecimal[] bigDecimalArray = new BigDecimal[10];
        Number[] numberArray = bigDecimalArray;
        bigDecimalArray = (BigDecimal[]) numberArray;
        BigInteger[] bigIntegerArray = (BigInteger[]) numberArray;  /* Fails at runtime. */
    }
  1. The first command BigDecimal[] bigDecimalArray = new BigDecimal[10] creates an array of type BigDecimal and of size 10. This is the ordinary way arrays are created.
  2. The second command Number[] numberArray = bigDecimalArray assigns this (more specific) BigDecimal array to a (more general) Number array. The compiler won’t complain, and this line of code will be executed fine.
  3. The third command bigDecimalArray = (BigDecimal[]) numberArray tries to “extract” the subtype BigDecimal array out of the supertype Number array. This is fine both during compile time and runtime. It uses the proper cast (BigDecimal[]).

    The main message of points 2 and 3 is this: Arrays are covariant, i.e., an array of a supertype (Super[]) is also a supertype of an array of a subtype (Sub[]). Or regarding the code example above: Number[] is a supertype of BigDecimal[], because Number is a supertype of BigDecimal.

  4. The last command BigInteger[] bigIntegerArray = (BigInteger[]) numberArray tries to extract the BigDecimal array out of the supertype Number array and cast it to a BigInteger array. Even though the compiler doesn’t complain, this line of code will fail during runtime. In this case, the compiler doesn’t provide compile time type safety.

Arrays are also called reified (probably the best synonym: “concretized”). They always have a “concrete”, known type during runtime.

Contrast this to generics (question 2), shown in the following example program:

    private void genericCollectionVariance() {
        List<Number> numberList = new ArrayList<>();
        List<BigDecimal> bigDecimalList = new ArrayList<>();
        numberList = bigDecimalList;  /* Fails at compile time. */
    }
  1. The first command List<Number> numberList = new ArrayList<>() creates a generic [Array]List of type Number. Note that I’m not talking about the hierarchy of List and ArrayList. Using the more general interface type List instead of ArrayList is considered more flexible style, so please don’t feel distracted. This is the ordinary way generic lists are created.
  2. The second command List<BigDecimal> bigDecimalList = new ArrayList<>() does the same for the more specific type BigDecimal.
  3. The last command numberList = bigDecimalList was intended to assign the more specific List<BigDecimal> to the more general List<Number>, analogous to step 2 in the array example above. However, the compiler complains with an error message: “Type mismatch: cannot convert from List<BigDecimal> to List<Number>”.

    The main message of point 3 is this: Generics are invariant, i.e., a generic collection of a supertype (List<Super>) is neither a supertype nor a subtype of a collection of a subtype (<Sub>). Or regarding the code example above: [Array]List<Number> is neither a supertype nor a subtype of [Array]List<BigDecimal>. They simply don’t have any hierarchy connection between each other.

Generics are also called non-reified (“non-concretized”). They don’t have a “concrete”, known type during runtime, for the reason given in the following section.

Erasure

The second principle one needs to know in order to understand why one cannot create generic arrays in a straightforward way is the concept of erasure. When generics were introduced in Java 5, it was of utmost importance to keep the bytecode backward compatible to versions prior to Java 5. Since the Java Virtual Machine (JVM; the “executor”) doesn’t and cannot know anything about generics, the Java designers came up with the idea of erasure. Erasure means that the generic type is simply “erased” by the compiler. Let’s see how this works.

The first example shows the source code of a user of a simple generic data structure, a SingleItemBag<T>, which simply stores one item of type T:

import java.math.*;

public final class Erasures {

    private void useSingleItemBag() {  /* before erasure */
        SingleItemBag<BigDecimal> bigDecimalBag = new SingleItemBag<>();
        bigDecimalBag.setItem(BigDecimal.ONE);
        BigDecimal myBigDecimal = bigDecimalBag.getItem();
        System.out.println(myBigDecimal);
    }

The user creates an instance of type BigDecimal, then sets an item, then gets the item again, and finally outputs its value. This is probably the most typical example of using generics as a “user”. The user doesn’t need to manually check types, doesn’t need to cast, and can rely on the compiler’s type safety. These are the great advantages of generics.

Things are getting interesting when you look at the code the compiler translates the previous version to:

    private void useSingleItemBag() {  /* after erasure */
        SingleItemBag bigDecimalBag = new SingleItemBag();
        bigDecimalBag.setItem(BigDecimal.ONE);
        BigDecimal myBigDecimal = (BigDecimal) bigDecimalBag.getItem();
        System.out.println(myBigDecimal);
    }

All generics (in other words, all writings between < and >) are gone; they were “erased”. However, the compiler now introduced an explicit type cast: BigDecimal myBigDecimal = (BigDecimal) bigDecimalBag.getItem(); Broadly speaking, the compiler changed the code to the way you would have written it in Java 1.4.

The second example shows the source code of the implementation of the generic data structure:

public final class SingleItemBag<T> {  /* before erasure */

    private T item;

    public SingleItemBag() {}

    public T getItem() {
        return item;
    }

    public void setItem(T item) {
        this.item = item;
    }
}

Again, the compiler removes everything generic:

public final class SingleItemBag {  /* after erasure */

    private Object item;

    public SingleItemBag() {}

    public Object getItem() {
        return item;
    }

    public void setItem(Object item) {
        this.item = item;
    }
}

Every type parameter T (which actually means: “I don’t know the specific type yet.”) gets replaced by Object. Since the compiler had control over the whole conversion process, it can guarantee that all type casts (which were also inserted by it) succeed. At the same time, the compiler becomes more strict. If there was the slightest chance that the use of generics results in a type mismatch, the compiler would have issued an error or a warning. By using generics, runtime type safety gets shifted towards compile time type safety.

The term “non-reified”—introduced in the previous section—explains the situation after erasure: All type information has been erased, so the JVM simply doesn’t know the types during runtime (they’re all Objects). Data types are no longer “concrete”.

Understanding the concept of erasure also answers the question why generics are invariant. If it were allowed that, e.g., a List<Number> “swallowed” a List<BigDecimal>, then both compile time type safety (because the compiler cannot see behind the “swallowing” in the source code) and runtime type safety (because of erasure, everything is Object) would be gone. In other words, type safety would be severly flawed, if not inexistent.

The Consequences of Erasure for Arrays

The original problem or question was why arrays cannot be created generically in a straightforward manner. Now that you know the main concepts of the unterlying theory, let’s try to create a generic array. The following code examples show a generic MultiItemBag<T> of type T, which is a data structure where one can add items of type T and get them back using indices. This is definitely no production code, but should suffice to demonstrate the concepts.

public final class MultiItemBag<T> {  /* before erasure */

    private T[] itemArray;

    private int indexToFirstFreeElement
            = 0;

    public MultiItemBag() {
        itemArray = new T[10];  /* Fails at compile time. */
    }

    public T getItem(int index) {

        /* [...] Parameter checks omitted. */

        T elementToReturn = itemArray[index];

        return elementToReturn;
    }

    public void addItem(T item) {

        /* [...] Array bound checks omitted. */

        itemArray[indexToFirstFreeElement++] = item;
    }
}

MultiItemBag is intended to use an array T[] itemArray as the internal data storage. However, the initialization itemArray = new T[10] fails due to a compiler error: “Cannot create a generic array of T”.

To understand why this is, have a look at the code that gets generated by the compiler after erasure:

public final class MultiItemBag<T> {  /* after erasure */

    private Object[] itemArray;

    private int indexToFirstFreeElement
            = 0;

    public MultiItemBag() {
        itemArray = new Object[10];
    }

    public Object getItem(int index) {

        /* [...] Parameter checks omitted. */

        Object elementToReturn = itemArray[index];

        return elementToReturn;
    }

    public void addItem(Object item) {

        /* [...] Array bound checks omitted. */

        itemArray[indexToFirstFreeElement++] = item;
    }
}

Every T got replaced by Object. Especially, the array T[] became an array Object[], and even worse, new T[10] became new Object[10]. When run, the JVM creates an array of the most general type, Object[]. Since one could put any object into Object[], it would undermine all type safety, especially when it came to extracting and casting back the elements (with the “automatic” casts inserted by the compiler). The flexibility and covariance of arrays simply doesn’t go together with the strictness, safety and invariance of generics.

In the following sections, I’ll show you four different ways to overcome this problem.

Fix 1: Use Collections Instead of Arrays

This fix sounds pretty lame, but it’s definitely justified. Since Java’s Collection API has grown so large during the last more than 20 years, think twice whether you really need an array to back your data. The Collection API contains all kinds of (linked) lists, stacks, queues, etc., even in specialized forms, with or without thread safety, with or without modification protection, etc.

import java.util.*;

public final class MultiItemBagFix1<T> {

    private List<T> itemList;

    public MultiItemBagFix1() {
        itemList = new ArrayList<>();
    }

    public T getItem(int index) {

        /* [...] Parameter checks omitted. */

        T elementToReturn = itemList.get(index);

        return elementToReturn;
    }

    public void addItem(T item) {
        itemList.add(item);
    }
}

Replacing the array in MultiItemBag[Fix1] by [Array]List<T> allows the use of generics in its pure and unrestricted form, simplifies the code (note that the index handling using indextoFirstFreeElement disappeared), offers much more functionality, and lets you use and rely on code that has been well-proven and tested for many years by millions of programmers.

Fix 2: Create and Cast the Array Object[] Manually

By creating an array Object[] manually—instead of letting the compiler do it by erasure—and casting it to the generic field type T[] itemArray, you “give a promise” to the compiler that you’re aware of what’s going on with this array and that you “take sole responsibility” if anything goes wrong. The compiler reminds you of this promise by issuing a warning: Type safety: Unchecked cast from Object[] to T[].

If you’re sure about what you’re doing, you can suppress the warning, as seen in the source code below:

public final class MultiItemBagFix2<T> {

    private T[] itemArray;

    private int indexToFirstFreeElement
            = 0;

    @SuppressWarnings("unchecked")
    public MultiItemBagFix2() {
        itemArray = (T[]) new Object[10];
    }

    public T getItem(int index) {

        /* [...] Parameter checks omitted. */

        T elementToReturn = itemArray[index];

        return elementToReturn;
    }

    public void addItem(T item) {

        /* [...] Array bound checks omitted. */

        itemArray[indexToFirstFreeElement++] = item;
    }
}

But what’s different compared to the original version? After erasure, doesn’t it look the same? Well, by manually inserting the type cast (T[]), the “responsibility” is transferred to you as the programmer. The creation of generic arrays is not disallowed because something will go wrong for sure, but rather because the compiler cannot guarantee that everything will be fine. By manually casting the array and suppressing (or ignoring) the warning, you have to live with the risk of ClassCastExceptions. Type safety can only be guaranteed by you as the programmer. If you’ve verified that during runtime the types won’t mess up, you can go with the fix provided above.

Fix 3: Use an Array Object[] and Cast Every Access Manually

This fix is similar to Fix 2 shown above. Whereas the former fix uses an array of type T[], thus eliminating the need for any type casts when accessing the array, the fix shown below kind of does the opposite: The array is kept in its general form, Object[], and every (read) access is accompanied by an explicit type cast (T). (There is no need for a cast when “writing” to the array, because everything can be written to Object.)

public final class MultiItemBagFix3<T> {

    private Object[] itemArray;

    private int indexToFirstFreeElement
            = 0;

    public MultiItemBagFix3() {
        itemArray = new Object[10];
    }

    public T getItem(int index) {

        /* [...] Parameter checks omitted. */

        @SuppressWarnings("unchecked")
        T elementToReturn = (T) itemArray[index];

        return elementToReturn;
    }

    public void addItem(T item) {

        /* [...] Array bound checks omitted. */

        itemArray[indexToFirstFreeElement++] = item;
    }
}

Whether one prefers Fix 2 or Fix 3 is a matter of taste. Both versions require explicit casts, thus shifting the responsibility of type safety to you as the programmer. Both versions require the suppression of warnings, of course not after you’ve verified unconditional type safety. If the class grew bigger and offered more access methods, Fix 2 would probably be better, because it only needs one type cast, whereas Fix 3 needs casts for every (reading) access method.

Fix 4: Use Reflection to Create the Generic Array

The last fix I’m presenting you uses Java’s reflection facility. The static method Array.newInstance(Class<?>, int) in the package java.lang.reflect creates an array of the specified type and the given length.

import java.lang.reflect.*;

public final class MultiItemBagFix4<T> {

    private T[] itemArray;

    private int indexToFirstFreeElement
            = 0;

    @SuppressWarnings("unchecked")
    public MultiItemBagFix4(Class<T> classType) {
        itemArray = (T[]) Array.newInstance(classType, 10);
    }

    public T getItem(int index) {

        /* [...] Parameter checks omitted. */

        T elementToReturn = itemArray[index];

        return elementToReturn;
    }

    public void addItem(T item) {

        /* [...] Array bound checks omitted. */

        itemArray[indexToFirstFreeElement++] = item;
    }
}

What sounds like a perfect solution actually has its drawbacks. First of all, newInstance needs the class type, i.e., the Class object of the desired type, handed in explicitly. It cannot find out the type on its own. Because of erasure, T.class would always result in Object, if it were allowed at all. (It’s not allowed, because it doesn’t make sense.) Always explicitly providing the class type makes using such a feature very cumbersome. See that the constructor of MultiItemBagFix4 requires its user to explicitly state the class type, even though users shouldn’t be confronted with such implementation details. Users of this data structure might possible ask themselves why the heck they must tell the class the type if it’s already specified by T. It’s very unusual, thus suspicious.

The second disadvantage is that one still needs an explicit type cast (as in Fix 2), still needs to take responsibility and double-check type safety (which might not be easy, since the user could also provide false class types as an argument), and still needs to suppress a warning.

Finally, using reflection is often considered bad, unsafe coding style, and usually doesn’t leave a good impression because of security, style, and performance reasons.

Checking the Provided Fixes

The following improvised test cases show that all four fixes actually work. Feel free to play around with the classes.

import java.math.*;

public final class BagFixChecks {

    public static void main(String[] args) {
        BagFixChecks bagFixChecks = new BagFixChecks();
        bagFixChecks.checkBagFix1();
        bagFixChecks.checkBagFix2();
        bagFixChecks.checkBagFix3();
        bagFixChecks.checkBagFix4();
    }

    private void checkBagFix1() {
        MultiItemBagFix1<BigDecimal> multiItemBag = new MultiItemBagFix1<>();
        multiItemBag.addItem(BigDecimal.TEN);
        multiItemBag.addItem(BigDecimal.ONE);
        multiItemBag.addItem(BigDecimal.ZERO);
        BigDecimal item0 = multiItemBag.getItem(0);
        BigDecimal item1 = multiItemBag.getItem(1);
        BigDecimal item2 = multiItemBag.getItem(2);
        System.out.format("MultiItemBagFix1: %2s, %2s, %2s%n", item0, item1, item2);
    }

    private void checkBagFix2() {
        MultiItemBagFix2<BigDecimal> multiItemBag = new MultiItemBagFix2<>();
        multiItemBag.addItem(BigDecimal.TEN);
        multiItemBag.addItem(BigDecimal.ONE);
        multiItemBag.addItem(BigDecimal.ZERO);
        BigDecimal item0 = multiItemBag.getItem(0);
        BigDecimal item1 = multiItemBag.getItem(1);
        BigDecimal item2 = multiItemBag.getItem(2);
        System.out.format("MultiItemBagFix2: %2s, %2s, %2s%n", item0, item1, item2);
    }

    private void checkBagFix3() {
        MultiItemBagFix3<BigDecimal> multiItemBag = new MultiItemBagFix3<>();
        multiItemBag.addItem(BigDecimal.TEN);
        multiItemBag.addItem(BigDecimal.ONE);
        multiItemBag.addItem(BigDecimal.ZERO);
        BigDecimal item0 = multiItemBag.getItem(0);
        BigDecimal item1 = multiItemBag.getItem(1);
        BigDecimal item2 = multiItemBag.getItem(2);
        System.out.format("MultiItemBagFix3: %2s, %2s, %2s%n", item0, item1, item2);
    }

    private void checkBagFix4() {
        MultiItemBagFix4<BigDecimal> multiItemBag = new MultiItemBagFix4<>(BigDecimal.class);
        multiItemBag.addItem(BigDecimal.TEN);
        multiItemBag.addItem(BigDecimal.ONE);
        multiItemBag.addItem(BigDecimal.ZERO);
        BigDecimal item0 = multiItemBag.getItem(0);
        BigDecimal item1 = multiItemBag.getItem(1);
        BigDecimal item2 = multiItemBag.getItem(2);
        System.out.format("MultiItemBagFix4: %2s, %2s, %2s%n", item0, item1, item2);
    }
}

Summary

First and foremost, don’t use generic arrays. Use Java’s Collection API instead, since chances are pretty low that it doesn’t contain at least the basic data structure of what you need (Fix 1). If you really cannot get around creating generic arrays, prefer Fix 2.

This blog post could have been about an academic problem, however, most programmers probably have been confronted with it at least once. This blog post showed the theoretical background of the problem: the fundamental difference of arrays versus generic collections with respect to variance, erasure, runtime type and compile time type safety. It again stresses the important fact that in order to really understand Java, one needs to have a thorough and detailed look at and understanding of its background.

Shortlink to this blog post: link.simplexacode.ch/rk572019.02

В этой статье мы представляем исчерпывающую статью, в которой объясняется проблема создания универсальных массивов. Язык программирования Java добавил дженерики в сентябре 2004 года в выпуске Java 5.0 «Tiger». Система обобщений или параметризации типов расширяет существующую систему типов Java, обеспечивая безопасность типов.

1. Введение

У Java есть Платформа Коллекций, предоставляющая библиотеку общих структур данных для использования в разработке программного обеспечения Java. В Collections Framework отсутствует одна структура данных — массив. Тем не менее, Java Collections Framework имеет типизированные структуры данных ArrayList.java и Vector.java. Обе структуры данных используют динамический массив одного измерения, который использует базовый массив java.lang.Object.

Java предоставляет встроенный массив, который является объектом, который включен в языковую спецификацию начиная с Java 1.0 с 1995 года. В качестве объекта встроенный массив объявляется и создается в Java-коде в виде частиц. Встроенный массив представляет собой контейнер объектов с фиксированным числом и длиной, возможно, многих измерений.

Тем не менее, безопасность типов во время компиляции с использованием дженериков не полностью реализована. Особенно со встроенным объектом массива.

2. Универсальные массивы

Проблема в том, что дженерики используются со встроенным объектом массива в Java. Рассмотрим следующий класс Java, TestArray1.java, который объявляет два универсальных массива вокруг одного параметра универсального типа E. Исходный код класса Java:

1

2

3

class TestArray1 {

  public E[] array = new E[10];

}

Исходный код для TestArray1.java является только декларативным, массивы не используются. Написано два варианта использования универсального массива: один для универсального массива в качестве атрибута класса, а другой — с использованием универсального массива в статическом (т. Е. Неэкземплярном) методе класса.

2.1 Общая ошибка при компиляции

При компиляции компилятор сообщает о следующей ошибке:

1

2

3

4

Error: TestArray1.java.                                                            

      Line 3 At 22: generic array creation                                           

        public E[] array = new E[10];                                                

                           ^            

Сообщается об одном виде ошибки: создание универсального массива. Эта ошибка напрямую связана с вариантом использования универсальных массивов.

ковариации

В Java массивы являются ковариантными или используют специализацию типа общего для определенных типов, таких как Коллекция в Набор. Однако параметры универсального типа не являются ковариантными. Гетц объясняет: «Классы Коллекций используют уродливую уловку, чтобы обойти эту проблему…» [Goet 2019]

Таким образом, чтобы использовать встроенный массив с универсальными типами Java или параметр универсального типа E, массив должен иметь тип java.lang.Object, который является отличным супертипом в Java. Все это java.lang.Object, это ужасный трюк.

Массив объектов

Тем не менее, недостаток использования массива Object является обязательным условием для обобщений — привязать структуру данных или переменную к определенному типу. Структура данных типа Object может смешивать и сопоставлять любой тип, и для преобразования в исходный тип требуется приведение типа. В этом дженерики в Java не полезны — и это основная проблема.

Решение или ответ на этот вопрос прост: универсальный класс массива Java. Такой класс отсутствует в структуре коллекций Java, поэтому создайте его.

3. Java Array Class

Класс Java Array похож на другие структуры данных в структуре коллекций Java, это структура данных. Первоначально написанная реализация предназначена для простоты и не реализует никаких конкретных интерфейсов или расширений каких-либо суперклассов. Цель состоит в том, чтобы получить функциональный и полезный массив в виде класса Array.java для построения. Скелет универсального параметра типа класса Array.java:

1

2

3

4

5

6

7

class Array {

    Array(final int... dim);

    void init(final E elem);                                                            

    void init(final E[] elem);

    E get(final int...idx);

    void add(final E elem, final int... idx);                                       

  }

По сути, у вас есть средство для создания массива любого ранга, а затем любого размера для измерений.

Массив затем имеет метод init () для инициализации массива начальным значением по умолчанию или начального значения. Наконец, массив имеет два основных метода: один для добавления () элемента и один для получения () элемента в определенном месте массива. Базовая функциональность создания экземпляра или создания, инициализации всего массива и доступа к элементу.

3.1 Атрибуты класса массива

Класс массива Java имеет несколько атрибутов, которые определяют экземпляр класса Array.java. Атрибуты параметра универсального типа Array.java:

1

2

3

4

5

6

7

class Array {

   int size;                                                                            

   int dim[];                                                                           

   int rank;                                                                               

   Object data[];                                                                   

   E elem;                                                                          

 }                        

Атрибуты класса Array.java представляют собой данные в виде встроенного массива Object, границы массива, такие как размер, ранг, размеры. Наконец, есть элемент, элемент инициализации.

3.2 ранг и размерность с Varargs

Язык программирования Java добавил переменную номер или параметры или аргументы, именованные переменные Java-аргументы, или, более просто, varargs в выпуске Java 5.0 «Tiger» в сентябре 2004 года. Эта особенность позволяет конструктору или методу принимать различное количество параметров, и, таким образом, обобщать параметры без дублирования конструктора или метода просто для количества параметров.

Обобщение класса Array использует эту особую функцию Java: varargs или переменные аргументы для создания и доступа к элементам в классе Array.java. Это допускает любой ранг (количество измерений), а также для каждого любого числового измерения для любого неотрицательного целого числа.

Varargs также позволяет рангу быть общим, поэтому нет (по крайней мере, теоретически…) верхней границы ранга класса Array.java. Таким образом, varargs допускают общее обобщение при определении и использовании универсального класса Array.java.

Конструктор с использованием Varargs

Исходный код для конструктора Array.java иллюстрирует использование varargs для измерений для создания универсального объекта массива с использованием класса Array.java. Ранг и измерения обобщены, поэтому любой ранг массива возможен с любыми размерными границами. Исходный код для конструктора Array.java:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

Array(final int... dims) {                                                          

  this.rank = dims.length;                                                          

  this.dim  = new int[rank];                                                        

  int size  = 1;                                                                    

  for (int x = 0; x < dims.length; x++) {                                            

    size = size * dims[x];                                                          

    dim[x] = dims[x];                                                                

  }

  this.data = new Object[size];                                                 

  this.size = size;                                                                  

}

Varargs — это переменные параметры, передаваемые как примитив int как массив целых чисел в переменной dims. Исходя из значения dim, вычисляются и создаются размеры для границ массива и общий внутренний «плоский» массив. Без varargs потребуется конструктор для каждого ранга для разных измерений. С конечным числом конструкторов для ранга класс универсального массива Array.java будет ограничен и не будет таким общим для varargs.

Accessor с Varargs

Доступ к элементу в общем массиве осуществляется через методы get и set. Это методы доступа или методы доступа. В отличие от методов получения и установки свойств, для универсального класса массива индекс измерения определяет элемент.

Используя varargs, это обобщает методы доступа для любого ранга или измерения для доступа к элементу. Индекс измерений проверяется на соответствие границ и ранга универсального массива для доступа.

Читатель Accessor Получить

Метод getter является средством доступа к чтению, он читает или копирует значение из массива. Исходный код для Array.java метод доступа get:

1

2

3

E get(final int... idx) {                                                           

  return (E) this.data[this.getIndex(idx)];                                     

}

Набор аксессуаров для записи

Метод setter является средством доступа к записи, он записывает или копирует значение в массив. Исходный код метода метода доступа Array.java:

1

2

3

void set(final E elem, final int... idx) {                                          

  this.data[this.getIndex(idx)] = elem;                                         

}

Для простоты вспомогательный метод getIndex () выполняет «тяжелую работу» по вычислению индекса одного измерения и проверке границ индекса массива.

Вспомогательные методы, чтобы помочь

Три вспомогательных метода или вспомогательные методы выполняют фактическую обработку в универсальном классе Array.java. Один метод getIndex () вычисляет один индекс из нескольких измерений индекса, а два других метода isValidDim () и isValidIndex () проверяют, что рассчитанный или заданный индекс не выходит за границы массива. Исходный код интерфейса для вспомогательных методов:

1

2

3

4

5

class Array {

  int getIndex(final int... idx);

  boolean isValidDim(final int... idx);                             

  boolean isValidIndex(final int idx);                                        

}

Тяжелая атлетика Получение индекса элемента

Метод getIndex () является основной функцией класса Array.java. Метод getIndex () вычисляет индекс во внутренний одномерный линейный или «плоский» массив элемента. От теории компилятора (и это намекает на теорию, но более подробное объяснение выходит за рамки объяснения), массив индексируется либо по маске строки, либо по столбцу майора для индекса. [Ахо 2007]

Для класса Array.java это несущественно, если функция согласована для заданного индекса для границ измерения экземпляра массива. Исходный код для метода getIndex ():

01

02

03

04

05

06

07

08

09

10

11

12

int getIndex(final int... idx){                                                         

  isValidDims(idx);                                                                 

  int index = 0;                                                                    

  for(int x = 0; x < idx.length; x++) {                                            

    int i = idx[x];                                                                   

    for(int y = x + 1; y < idx.length; y++) {                                        

      i = i * dim[y];                                                                   

    }

    index = index + i;                                                                

  }

  return index;                                                                         

}

Исходный код метода getIndex () проверяет размеры индекса перед тем, как фактически рассчитать одномерный индекс во внутренний линейный массив.

Валидация индекса

Есть два метода для проверки индекса. Одним из них является проверка многомерного индекса, а другим — проверка одного индекса. Исходный код для проверки одномерного индекса:

1

2

3

void isValidIndex(final int idx){                                                       

   if(idx = this.size) throw new RuntimeException("Index Overflow Error!");            

 }

IsValidIndex () просто проверяет, что индекс математически находится в диапазоне от нуля до общего размера внутреннего массива. Если индекс не находится в пределах диапазона, генерируется непроверенное исключение во время выполнения. Исходный код для проверки многомерного индекса:

1

2

3

4

5

6

void isValidDims(final int... idx) {                                                  

  if(idx.length != this.dim.length) throw new RuntimeException("Rank Error");      

    for(int x = 0; x = dim[x]) throw new RuntimeException(“Index Overflow Error");        

      if(idx[x] < 0) throw new RuntimeException(“Index Underflow Error”);                           

    }

}

IsValidDims () просто обходит каждый параметр измерения и проверяет, что ранг индекса совпадает с рангом параметра массива. Если ранг массива и индекса не равны, генерируется непроверенное исключение RuntimeException.

3.3 Другие неарагские методы

Другие методы не являются varargs, которые либо не принимают параметры в качестве метода доступа геттера, либо принимают один параметр. Две категории методов:

  1. Запрос параметров массива
  2. Уникальная функциональность массива

Запросить массив

Запрос параметров массива обращается к параметрам массива, как метод доступа геттера или с параметром. Массив представляет собой запросы на ранг, общий размер, верхние измерения и измерение по определенному индексу в ранге. Интерфейс для запроса методов массива класса Array.java:

1

2

3

4

5

6

class Array {

  int getDim(final int dim);

  int[] getDims();

  int getRank();

  int size();                                                           

}

Уникальная функциональность класса Array

Уникальная функциональность класса массива — это конструктор и два метода, которые предоставляют уникальную функциональность. Эти два метода предназначены для доступа и преобразования в линейный массив экземпляра класса Array. Другая функциональность — это конструктор, который позволяет копировать или реплицировать экземпляр класса Array. Функциональность для класса Array.java:

1

2

3

4

5

class Array {

  Array(final Array array);

  E getAt(final int idx);

  Object[] toArray();                              

}    

Accessor as Linear Array

Метод getAt () позволяет получить доступ к элементу массива, как если бы экземпляр класса Array представлял собой «плоский» линейный массив одного измерения. Целочисленный индекс проверяется на достоверность, а элемент возвращается в правильном месте с внутренним одномерным массивом. Исходный код для доступа в виде линейного массива:

1

2

3

4

E getAt(final int index) {                                                            

  this.isValidIndex(index);                                                    

  return (E) this.data[index];                                                  

}

Преобразование в массив линейных объектов

Метод toArray () преобразует экземпляр класса Array или, скорее, обращается к внутреннему одномерному массиву и возвращает его в виде массива Object. Метод toArray () возвращает поверхностную копию внутреннего линейного массива, а не глубокую копию. Исходный код средства доступа геттера для доступа к массиву линейных объектов:

1

2

3

Object[] toArray() {                                                                   

  return this.data;                                                             

}

Копировать конструктор

Конструктор копирования позволяет дублировать экземпляр класса Array, но в качестве «глубокой» копии существующего экземпляра класса Array. Скопированный массив и копия имеют один и тот же тип параметра, размеры, ранг и, конечно, элементы. Исходный код конструктора копирования:

1

2

3

4

5

6

7

8

Array(final Array orig) {                                                             

  this.rank = orig.rank;                                                                      

  this.dim  = orig.dim;                                                                

  this.size = orig.size;                                                              

  this.elem = (E) orig.elem;                                                   

  this.data = new Object[this.size];                                         

  System.arraycopy(orig.data, 0, this.data, 0, this.size);                    

}

System.arraycopy () копирует оригинал и создает новый массив Object для глубокой копии. Различные параметры экземпляра класса Array копируются в глубокую копию.

4. Использование универсального класса Array

Использование универсального массива Java Array.java иллюстрируется в исходном коде двумя примерами использования:

  1. Алгоритм пузырьковой сортировки
  2. Таблица умножения

Оба примера иллюстрируют демонстрацию использования универсальных методов класса массива для создания, инициализации, доступа, запроса и реализации функциональных возможностей массива, но без использования встроенного массива Java. Исходный код иллюстрирует, и вывод от фрагмента исходного кода дается.

4.1 Bubble Sort

Сортировка по пузырькам — это простой и простой алгоритм сортировки, но он идеально подходит для иллюстрации использования универсального класса массива Array.java. Пузырьковая сортировка реализована с использованием универсального массива Java:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

Array list = new Array(9).init(new Integer[]{3,5,7,4,8,0,2,1,6}); 

System.out.println(Arrays.toString(list.toArray()));                                 

boolean swapFlag = true;                                                              

while(swapFlag) {                                                                      

  swapFlag = false;                                                                   

  for(int x=0;x 0) {                                    

      Integer temp = list.get(x);                                                      

      list.set( list.get(x+1), x);                                                     

      list.set( temp, (x+1));                                                        

      swapFlag = true;                                                                 

    }

  }

}

System.out.println(Arrays.toString(list.toArray()));

При запуске вывод из пузырьковой сортировки с использованием универсального массива Java:

1

2

3

[3, 5, 7, 4, 8, 0, 2, 1, 6]                                                             

[0, 1, 2, 3, 4, 5, 6, 7, 8]                                                               

4.2 Таблица умножения

Основное иллюстративное применение универсального массива Java состоит в создании простой таблицы умножения целого числа от 1 до 10. Для этого требуется двумерный массив целых чисел. После создания таблицы умножения проверяются математические свойства тождества и коммутативности. Исходный код для этого использования универсального массива Java:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

Array multiplyTable = new Array(10,10).init(0);                   

for(int x=0;x<multiplyTable.getDim(0);x++){                                           

  for(int y=0;y<multiplyTable.getDim(1);y++){                                           

    multiplyTable.set(x*y, x,y);                                                

  }

}

for(int x=0;x<multiplyTable.getDim(0);x++){                                          

  if(multiplyTable.get(1,x) != x)                                                    

    throw new RuntimeException("Identity property doesn't hold!”);                    

  if(multiplyTable.get(x,1) != x)                                                   

    throw new RuntimeException("Identity property doesn't hold!”);                    

}

for(int x=0;x<multiplyTable.getDim(0);x++){                                              

  for(int y=0;y<multiplyTable.getDim(1);y++){                                            

    if(multiplyTable.get(x,y) != multiplyTable.get(y,x) )                              

      throw new RuntimeException("Commutative property doesn't hold!");                   

  }

}

Нет выходных данных, потому что тождество и коммутативные математические свойства для умножения верны и, следовательно, действительны. Но это иллюстрирует использование двумерного универсального массива. Для других более высоких измерений возможны другие применения.

5. Заключение

Первоначальная проблема продемонстрировала проблему с параметризованным типом или универсальным массивом с использованием встроенного объекта массива. Используя тот же фрагмент кода, но заменяя общий массив Array.java, исходный код:

1

2

3

class TestArray2 {                                                                                                                                                                                                                  

  public Array array = new Array(10);                                                                                                                                                                                             

}

При компиляции нет сообщений об ошибках. Структура данных Array.java позволяет решить исходную проблему.

Параметризация типов или обобщений в Java допускает безопасные с точки зрения типов классы, но при разработке системы параметризации обобщенных типов существуют компромиссы. Таким образом, дженерики в Java имеют некоторые недостатки, связанные со встроенной сущностью массива Java. К сожалению, Java Collections Framework не устраняет проблему, предоставляя структуру данных массива.

Решение находится в языке Java и параметризации универсального типа. Просто спроектируйте универсальный класс Array как первоклассный объект, написанный на Java. На первый взгляд, это избыточное дублирование существующего объекта Java — массива.

Это не репликация; Проектирование класса Java Array создает общую, безопасную для типов структуру данных, которая является полезной заменой встроенной сущности массива Java. Компромисс заключается в том, что без перегрузки операторов синтаксис менее явный в отношении доступа к массиву и операций, но он согласуется с вызовом методов для экземпляра объекта.

6. Ссылки

  • [Ахо 2007] Ахо, Альфред В., Лам, Моника С., Сетхи, Рави и Уллман, Джеффри Д. Компиляторы: принципы, методы и инструменты, 2-е издание. Pearson Education, Inc., Нью-Йорк, Нью-Йорк, 2007, с. 381 — 382.
  • [Goet 2019] Гетц, Брайан. «Теория и практика Java: общие черты», 25 января 2005 г.

7. Загрузите исходный код

Overview

An array can be simply defined as a collection of items stored in contiguous memory locations. It is one such concept that every programmer, novice or expert is quite aware of but the Generic Array in Java is something that might be a new concept for you if you are new to Java programming. This article is about how to make a generic array in Java. We will look into arrays and Java Generics and will extensively discuss the java generic array creation process and why would we need it.

new java job roles

The Java Generics

Java Generics were introduced in JDK 5.0 primarily aiming to reduce errors in Java and to add an extra layer of abstraction over data types. This feature was indeed very helpful for developers as it removes repetitive casting in the code.

See this simple piece of code used to create a simple list in Java to store Integer type values:

List list = new LinkedList();
list.add(new Integer(0));
Integer index = list.iterator().next();

The compiler will result in an error in the last line as it would be unaware of what data type is to be returned. No line in this code guarantee that the return type of this list is Integer. The defined list could be holding any type of object that is why an explicit cast is required to ensure the data type.

To resolve the error, it requires a line of code with explicit casting like this:

Integer index = (Integer) list.iterator.next();

Such explicit casting statements often clutter your code and can also cause type-related runtime errors due to any unintentional mistakes. It would be much more convenient if the intention to use a specific data type could be mentioned so the compiler can directly ensure the correctness of such types. Java generics are used to cater to this issue.

See this code below, implementing the generics by changing the first line of the previous code snippet:

List<Integer> list = new LinkedList<>();

The addition of the diamond operator <> containing the required data type narrows down the specialization of this list to only Integer type. In other words, it specifies the type of data held inside the list and now the compiler can enforce this data type at compile time. This can make your significantly easier to read and manage.

A generic array is different from a normal array in Java as it is initially made independent of any data type and the data type is eventually evaluated by the compiler at runtime.

The biggest difference between arrays and a generic array is of enforcing the type checking. Arrays usually store and check data type information at runtime whereas a Generic would check for data type at compile time and  the data type information would not even be available at runtime. Using the Java generic syntax mentioned before, we might be able to create a new generic array like this.

T[] elements = new T[size];

However, if we attempt this for a Java generic array creation, it will result in a compile time error because the information needed at the runtime for memory allocation will not be available.

Now, Consider the following complete code demonstration for a Java generic array creation

public <T> T[] myArr (int n)
{
    T[] genericsArray = new T[n];
    return genericsArray;
}

And the following method will be used at runtime:

public Object[] myArr(int n)

{
    Object[] genericsArray = new Object[n];
    return genericsArray;
}

Now,  if we call this method and try to store the result in an array of integer data type like this,

Integer[] myArr01 = myArr (5);

It will compile just fine but will result in a runtime error with a ClassCastException. It is because of assigning an Object[] to an Integer[] reference. The implicit casting done by the compiler would fail in converting an Object[] to our required type Integer[].

Creating a Generic array in Java

Considering all these previous points, it is quite evident that there is no straightforward method for Java generic arrays creation, but it is still possible to achieve this by some indirect methods.

The following two array-like structures can be used instead to simulate a Java generic Array.

– Using Object Array

This approach includes the use of an array of Objects type as a member of the main array class. We instantiate the main array class that can provide the data type as required along with that, getter and setter methods can be used to read or set the elements in the array. This can simulate the working of a Java generic array.

See this code demonstration below:

import java.util.Arrays;
class Array<T> {
    private final Object[] object_array;   //object array
    public final int len;
    // class constructor
    public Array(int len)

{
        // instantiate a new Object array of specified length
        object_array = new Object [len];
        this.len = len;
    }
    // getter method
    T getArr(int i)
    {
        @SuppressWarnings("unchecked")
        final T t = (T)object_array[i];
        return t;
    }

    // setter method

    void setArr(int i, T t)
    {
        object_array[i] = t;
    }
    @Override
    public String toString() {
        return Arrays.toString(object_array);
    }
}
class Main {
    public static void main(String[] args){
        final int length = 10;
        // creating an integer array
        Array<Integer>int_Arr = new Array(length);
        System.out.print("Integer generic array:" + " ");
        for (int i = 0; i < length; i++)
            int_Arr.set(i, i + 1);
        System.out.println(int_Arr);
        // creating a string array
        Array<String>str_Arr = new Array(length);
        System.out.print("String Generic Array:" + " ");
        for (int i = 0; i < length; i++)
            str_Arr.set(i, String.valueOf((char)(i + 65)));
        System.out.println(str_Arr);
    }
}
Output:
Integer Generic Array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
String Generic Array: [A, B, C, D, E, F, G, H, I, J]

In the above code demonstration, A generic class array is defined. The object array is a member of the class, instantiated with a constructor and a length variable. The generic getter and setter methods are used to read and set an array element of a particular type.

While creating instances, we can specify the desired type. The two arrays of type Integer and String are then created and populated and at last, the contents of each of these instances are displayed using the overridden ‘toString’ method.

– Using Reflection class

Another method is to use a reflection class to get the working of a Java generic array whose type will only be known at the runtime. It is quite a similar approach to the previous one but with just one difference, here, the reflection class is used in the constructor itself to instantiate an object array by explicitly passing the data type information to the class constructor. It is passed to the Array.newInstance method of reflection.

The following code demonstrates the usage of reflection to create a Java generic array:

Import java.util.Arrays; 
class Array<T>
{
    private final T[] object_Arr;
    public final int length;
    // class constructor
    public Array(Class<T>dataType, int length)
    // creating a new array with the specified data type and length at runtime using reflection
    this.object_Arr = (T[]) java.lang.reflect.Array.newInstance(dataType, length);
        this.length = length;
    }
    // getter method
    Tget(int i) {
        Return object_Arr [i];
    }
    // assigning t to object_Arr [i]
    void setter(int i, T t) {
        object_Arr[i] = t;
    }
    @Override
    public String toString() {
        return Arrays.toString(object_Arr);
   }
}
class Main
  {
     public static void main(String[] args)
    {
        final int length = 6;
        // creating an array with Integer as data type
        Array<Integer>int_Arr = new Array(Integer.class, length);
        System.out.print("Integer Generic Array:" + " ");
        for (int i = 0; i < length; i++)
            int_Array.set(i, i + 5);
        System.out.println(int_Arr);
        // creating an array with String as data type
        Array<String>str_Arr = new Array(String.class, length);
        System.out.print("String Generic Array:" + " ");
       
        for (int i = 0; i < length; i++)
            str_Array.set(i, String.valueOf((char)(i + 97)));
        System.out.println(str_Arr);
    }
  }
Output:

Integer Generic Array: [5, 10, 15, 20, 25, 30]

String Generic Array: [a, b, c, d, e, f]

Conclusion

The Java Generic array cannot be directly created as we cannot have a parameterized type assigned to an array reference. We looked into the reasons behind why it cannot be done However, there are some indirect ways to utilize the functionality of genetics with Arrays using object arrays and reflection features.

Wrapping it up, we can say that arrays and generics do not go hand in hand in Java as arrays are covariant while generics are invariant but Java, being such a dynamic language, it also offers some great alternatives to get the task done.

Also Read: Java Expressions with Examples

new Java jobs

To understand this topic let us directly start with an example.

List<Integer> arrayOfIntegerList[] = new ArrayList<>[10]; // compile time error !!

You will find that a simple statement like this will not even compile because the Java compiler does not allow this. To understand the reason, you first need to know two arrays are covariant and generics are invariant.

Covariant: It means you can assign subclass type array to its superclass array reference. For instance,

Object objectArray[] = new Integer[10]; // it will work fine

Invariant: It means you cannot assign subclass type generic to its super class generic reference because in generics any two distinct types are neither a subtype nor a supertype. For instance,

List<Object> objectList = new ArrayList<Integer>(); // won't compile

Because of this fundamental reason, arrays and generics do not fit well with each other.

Now let’s get back to the actual question. If generic array creation were legal, then compiler generated casts would correct the program at compile time but it can fail at runtime, which violates the core fundamental system of generic types.

Let us consider the following example to understand that:-

1) List<Integer> arrayOfIdList[] = new ArrayList<Integer>[10];// Suppose generic array creation is legal.
2) List<String> nameList = new ArrayList<String>();
3) Object objArray[] = arrayOfIdList; // that is allowed because arrays are covariant
4) objArray[0] = nameList;
5) Integer id = objArray[0].get(0);

As we assumed generic array creation is legal, so line 1 is valid and creates an array of ID List.

In line 2, we have created a simple list of string.

In line 3, we passed an arrayOfIdList object to objArray reference, which is legal because arrays are covariant.

In line 4, we have assigned nameList (i.e. the list of string) into objArray that is pointing to the arrayOfIdList object. It is alright because of Type Erasure, means the runtime instance of List<String> is List and List<Integer> arrayOfIdList[] is list[], so this will not generate any exception. Here comes the biggest problem, we have assigned the list of string (i.e., List<String>) into an array that can only contain the list of integer.

In line 5, mistakenly, we are trying to get the first element of the 0th element of an array. As arrayOfIdList declared as an array of integer list, the compiler will cast that assignment to Integer which will generate ClassCastException at runtime.

Here one of the major purposes of generic is failed (i.e., compile time strict type checking.) Therefore, to address this problem compile time error gets generated at line 1.

Reference: Effective Java 2nd edition

Arrays of Parameterized Types

You cannot create an array of type-specific generic references. Attempting to create an array of type-specific generic, causes a compiler error.  For example, the following statement is not permitted and results in a compiler error message:

Arrays of Parameterized Types

This code is the simplest approach to creating an array of a generic type and the compiler tells you explicitly that creating a generic type array is forbidden. Why Java does not allow you to create arrays of generic types ? The first thing we need to do is recall how arrays work for regular Java types. An array is a kind of built-in collection of some base type of element. Furthermore, array types are true types in the Java language and are represented at runtime by unique class types.  This is where the trouble begins. Although arrays in Java act a lot like generic collections, they do not behave like Java generics with respect to their type relationships.

Arrays differ from generic types in two important ways. First, arrays are covariant, which means simply that if Sub is a subtype of Super, then the array type Sub[] is a subtype of Super[]. Generics, by contrast, are invariant: for any two distinct types Type1 and Type2, Gen<Type1> is neither a subtype nor a supertype of Gen< Type2>. You might think this means that generics are deficient, but arguably it is arrays that are deficient. For example:

Arrays of Parameterized Types

Either way you can’t put a String into a Integer container, but with an array you find out that you’ve made a mistake at runtime; with a Gen, you find out at compile time. Of course you’d rather find out at compile time. The second major difference between arrays and generics is that arrays are reified. This means that arrays know and enforce their element types at runtime. As noted above, if you try to store a String into an array of Integer, you’ll get an ArrayStoreException. Generics, by contrast, are implemented by erasure. This means that they enforce their type constraints only at compile time and discard (or erase) their element type information at runtime.

Because of these fundamental differences, arrays and generics do not mix well. For example, it is illegal to create an array of a generic type, a parameterized type, or a type parameter. None of these array creation expressions are legal: new Gen<T>[], new Gen<Intgeer>[], new T[]. All will result in generic array creation errors at compile time. Why is it illegal to create a generic array? Because it isn’t typesafe. If it were legal, casts generated by the compiler in an otherwise correct program could fail at runtime with a ClassCastException. This would violate the fundamental guarantee provided by the generic type system. Consider the following code fragment that demonstrates why generic array creation is illegal – won’t compile:

Arrays of Parameterized Types

Let’s pretend that line 1, which creates a generic array, is legal. Line 2 stores the Gen<Integer> array into an Object array variable, which is legal because arrays are covariant. Line 3 creates and stores the Gen<String> into the sole element of the Object array, which succeeds because generics are implemented by erasure: the runtime type of a Gen<String> instance is simply Gen, and the runtime type of a Gen<Integer>[] instance is Gen[]:

Arrays of Parameterized Types

So line 3 assignment doesn’t generate an ArrayStoreException. Now we’re in trouble. We’ve stored a Gen<String> instance into an array that is declared to hold only Gen<Integer> instances. In line 4, we retrieve the element from the sole gen object in this array. The compiler automatically casts the retrieved element to Integer, but it’s an String, so we get a ClassCastException at runtime. In order to prevent this from happening, line 1 (which creates a generic array) generates a compile-time error.

Note that only the creation of generic arrays is outlawed. You can declare a variable of array of generic type, Gen<Integer>[] is legal. But you can’t initialize it with a new Gen<Integer>[3]. By contrast, you can initialize it with an array of raw type, new Gen[3], but it is not safe. For example:

Arrays of Parameterized Types

Let’s pretend that line 1, which creates a raw type of Gen array and stores that array into an Gen<Integer> array variable. In line 2, we retrieve the sole element from the sole Gen object in this array. The compiler automatically casts the retrieved element to Integer, but it’s an String, so we get a ClassCastException at runtime. So, creation of an array of raw type is not safe. If you need to collect parameterized type objects, simply use an java.util.ArrayList: ArrayList<Gen<Integer>> is safe and effective.

Program

Arrays of Parameterized Types

Program Source

class Gen<T> {
    
    private T t1;

    Gen(T t) {
        t1 = t;
    }
    T getT() {
        return t1;
    }
}

public class Javaapp {

    public static void main(String[] args) {
        
        Gen anygenarray[] = new Gen[3];
        anygenarray[0] = new Gen<String>("Java");
        anygenarray[1] = new Gen<Integer>(50);
        anygenarray[2] = new Gen<Float>(26.5f);
 
        Gen<Integer> genintarray[] = anygenarray;
        Integer getint = genintarray[0].getT();
    }
}

Понравилась статья? Поделить с друзьями:

Читайте также:

  • Generic application error enable debug output for detailed information карос
  • Generic application error enable debug output for detailed information как исправить
  • Generic application error enable debug output for detailed information mount and blade
  • Generator werkstatt multivan ошибка
  • Generationtarget encountered exception accepting command error executing ddl via jdbc statement

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии