Array of Generic List

Convert a generic list to an array

You can just call list.toArray(T[] array) and not have to worry about implementing it yourself, but as aioobe said, you can't create an array of a generic type due to type erasure. If you need that type back, you need to create a typed instance yourself and pass it in.

Error about creating array of Generic List in Java

First Code

List<Integer>[] array = (List<Integer>[]) new Object[size]; 

The reason why the first code fails is because casting does not change the actual type of the array, it just makes the compiler accept the code as valid. Imagine if you had another reference to the underlying object array:

final int size = 2;
Object[] objectArr = new Object[size];
List<Integer>[] integerArr = (List<Integer>[]) objectArr; // Does not work
objectArr[0] = "foobar";
List<Integer> i = integerArr[0]; // What would happen ??

The above code compiles fine, since you are forcing the compiler to accept it with the cast. But you can already see why it would be a problem for the cast to work at runtime: you would end up with an List<Integer>[] that now contains a String, which makes no sense. So the language disallows this.

Second Code

E[] array = (E[]) new Object[size];

Generics in Java are kind of odd. For various reasons, such as backwards compatibility, generics are basically erased by the compiler and will (mostly) not appear in the compiled code (Type Erasure). Instead, it will use a series of rules (JLS spec) to determine what type should be used in the code instead. For a basic unbouded generic; this type will be Object. So, assuming there is no bound on E, the second code is changed by the compiler to this:

 Object[] array = (Object[]) new Object[size];

So since both arrays have the exact same type after the erasure, there is no problem at runtime and the cast is basically redundant.

It is worth noting that this only works as long as E is unbounded. For example, this will fail at runtime with a ClassCastException:

public static <E extends Number> void genericMethod() {
final int size = 5;
E[] e = (E[]) new Object[size];
}

That is because E will be erased to Number, and you will get the same problem as the first code:

Number[] e = (Number[]) new Object[size];

It is important to keep the erasure in mind when working with code. Otherwise you may run into situations where the code acts different from what you expect. For example, the following code compiles and runs without exceptions:

public static <E> void genericMethod(E e) {
final int size = 2;
Object[] objectArr = new Object[size];
objectArr[0] = "foobar";

@SuppressWarnings("unchecked")
E[] integerArr = (E[]) objectArr;
integerArr[1] = e;

System.out.println(Arrays.toString(integerArr));
System.out.println(e.getClass().getName());
System.out.println(integerArr.getClass().getName());
}

public static void main(String[] args) {
genericMethod(new Integer(5)); // E is Integer in this case
}

Third Code

List<Integer>[] array = (List<Integer>[]) new ArrayList[size];

Similarly to the case above, the third code will be erased to the following:

 List[] array = (List[]) new ArrayList[size];

Which is no problem because ArrayList is a subtype of List.

Fourth Code

List<Integer>[] array = new ArrayList<Integer>[size];

The above will not compile. The creation of arrays with a type that has a generic type parameter is explicitely disallowed by the spec:

It is a compile-time error if the component type of the array being initialized is not reifiable (§4.7).

A type with a generic parameter that is not an unbounded wildcard (?) does not satisfy any condition for reifiability:

A type is reifiable if and only if one of the following holds:

  • It refers to a non-generic class or interface type declaration.
  • It is a parameterized type in which all type arguments are unbounded wildcards (§4.5.1).
  • It is a raw type (§4.8).
  • It is a primitive type (§4.2).
  • It is an array type (§10.1) whose element type is reifiable.
  • It is a nested type where, for each type T separated by a ".", T itself is reifiable.

Java 1.6: Creating an array of List T

Well, generics tutorial give the answer to your question.

The component type of an array object
may not be a type variable or a
parameterized type, unless it is an
(unbounded) wildcard type.You can
declare array types whose element type
is a type variable or a parameterized
type, but not array objects.

This is
annoying, to be sure. This restriction
is necessary to avoid situations like:

// Not really allowed.
List<String>[] lsa = new List<String>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// Unsound, but passes run time store check
oa[1] = li;

// Run-time error: ClassCastException.
String s = lsa[1].get(0);

If arrays of parameterized type were
allowed, the previous example would
compile without any unchecked
warnings, and yet fail at run-time.
We've had type-safety as a primary
design goal of generics.

Typescript: Using an array of generics to create an array of object with generic in the order of the generic list

You need to create a mapped type from T where there indexes of the array are the keys, and the values are the class wrapping the entry at that index.

class Class<T extends any[]> {
public readonly parameter: {
[Index in keyof T]: OtherClass<T[Index]>
}
}

Now the rest works just like you expect:

const c = new Class<[string, number]>();
const a1: string = c.parameter[0].something;
const a2: number = c.parameter[1].something;

const c1 = new Class<[number, string, SomeClass, SomeOtherClass]>();
const b1: number = c1.parameter[0].something;
const b2: string = c1.parameter[1].something;
const b3: SomeClass = c1.parameter[2].something;
const b4: SomeOtherClass = c1.parameter[3].something;

Playground

Java generics in ArrayList.toArray()

If you look at the implementation of toArray(T[] a) of ArrayList<E> class, it is like:

public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}

Problem with this method is that you need to pass array of the same generic type. Now consider if this method do not take any argument then the implementation would be something similar to:

public <T> T[] toArray() {
T[] t = new T[size]; // compilation error
return Arrays.copyOf(elementData, size, t.getClass());
}

But the problem here is that you can not create generic arrays in Java because compiler does not know exactly what T represents. In other words creation of array of a non-reifiable type (JLS §4.7) is not allowed in Java.

Another important quote from Array Store Exception (JLS §10.5):

If the component type of an array were not reifiable (§4.7), the Java Virtual Machine could not perform the store check described in the
preceding paragraph. This is why an array creation expression with a
non-reifiable element type is forbidden (§15.10.1).

That is why Java has provided overloaded version toArray(T[] a).

I will override the toArray() method to tell it that it will return an
array of E.

So instead of overriding toArray(), you should use toArray(T[] a).

Cannot Create Instances of Type Parameters from Java Doc might also be interesting for you.

c# printing an array list which is made of generic lists

The below will give you a single dimensional list you can use foreach to print out each item.

List<tempClass> tests = arrayList.ToArray()
.Select(obj => (List<tempClass>)obj)
.Aggregate((first, second) => { second.AddRange(first.ToArray()); return second; });

foreach(tempClass test in tests)
{
Console.WriteLine(test.Name);
}

How to create a generic array in Java?

I have to ask a question in return: is your GenSet "checked" or "unchecked"?
What does that mean?

  • Checked: strong typing. GenSet knows explicitly what type of objects it contains (i.e. its constructor was explicitly called with a Class<E> argument, and methods will throw an exception when they are passed arguments that are not of type E. See Collections.checkedCollection.

    -> in that case, you should write:

    public class GenSet<E> {

    private E[] a;

    public GenSet(Class<E> c, int s) {
    // Use Array native method to create array
    // of a type only known at run time
    @SuppressWarnings("unchecked")
    final E[] a = (E[]) Array.newInstance(c, s);
    this.a = a;
    }

    E get(int i) {
    return a[i];
    }
    }
  • Unchecked: weak typing. No type checking is actually done on any of the objects passed as argument.

    -> in that case, you should write

    public class GenSet<E> {

    private Object[] a;

    public GenSet(int s) {
    a = new Object[s];
    }

    E get(int i) {
    @SuppressWarnings("unchecked")
    final E e = (E) a[i];
    return e;
    }
    }

    Note that the component type of the array should be the erasure of the type parameter:

    public class GenSet<E extends Foo> { // E has an upper bound of Foo

    private Foo[] a; // E erases to Foo, so use Foo[]

    public GenSet(int s) {
    a = new Foo[s];
    }

    ...
    }

All of this results from a known, and deliberate, weakness of generics in Java: it was implemented using erasure, so "generic" classes don't know what type argument they were created with at run time, and therefore can not provide type-safety unless some explicit mechanism (type-checking) is implemented.

Converting a generic List to an Array. Why do I need use clone?

My question is since I already made the list from the varargs, why do I have to do items.clone()

You are right. Unfortunately, the compiler will be unable to determine the type of the array if you simply use the toArray() method. You should get a compilation error saying Cannot convert from Object[] to T[]. The call to item.clone() is required to assist the compiler in type-inference. An alternate approach would be to say return (T[])list.toArray

That said, I would not recommend either of the approaches. It doesn't really make sense to convert an array to a list and convert it back to an array in the first place. I don't see any significant take-aways that you would even understand from this code.



Related Topics



Leave a reply



Submit