Abstract Data Type: Array of objects - java

So I have this class, and I want to method that takes in an int, and creates a new array of that size. If I
declare
newArray<Object<int,int>> array1 = new newArray<Object<int,int>>(10);
This would create an array of size 10.
I've tried doing
public class newArray<O>
{
private O[] array;
public newArray(int newArraySize)
{
newArray<O>[] = new newArray<O>[newArraySize];
}
}
but I get an " Cannot create a generic array of newArray " error.

Unfortunately, you can't create a generic array. This is due to the way generics are implemented in Java, which is through type erasure. In effect, all generic types are "erased" to their bounding type (usually Object) before compilation, so all the compiler sees are Object instead of T or E or O. The erasure process generates automatic casts to ensure that the program would still work as intended.
This means that you can't do:
new O() (this gets erased to new Object(), which the compiler has no idea what to do with)
new O[] (this gets erased to new Object(), which again isn't helpful to the compiler)
What you can do is casting:
array = (O[]) new Object[size];
And in fact, that's how it's done in Java's Collections framework. You'll get an "unchecked" warning, as the compiler can't prove that this conversion is safe, but there really is no other option.
Also, few things I want to point out about your question, that you may or may not know already:
You can't use primitives as type parameters
Object has no type parameters
newArray<O>[] = new newArray<O>[newArraySize]; should really be array = new newArray<O>[newArraySize];. You already declared the array you wanted to use. So use it!
Looks like you're implementing your own ArrayList, in fact. If you are, good luck! If you aren't, you should really be using the existing implementation, unless you need some special behavior that you can't get otherwise...

You are almost there.
Take a look at the following code for an example on how to initialize, populate, and print an array.
import java.util.*;
public class Test
{
public static void main(String[] args)
{
// define the array
List<Integer> array1 = new ArrayList<Integer>(10);
// initialize the array with some value. In this case, the Integer "200"
// note that the array doesn't have a hard limit of 10 as define above. You can change the following value to 20 and it will still work
for (int i = 0; i < 10; i++)
{
array1.add(i, 200);
}
// print the array
System.out.println(array1);
}
}

Does this help?
ArrayList<Integer[][]> r = new ArrayList<Integer[][]>(10);
// You could replace the above list with a custom list
Integer[][] ob = new Integer[1][2];
ob[0][0] = 10;
ob[0][1] = 20;
r.add(ob);
for(Integer[][] o : r)
for(Integer[] o1 : o)
for(Integer o2 : o1)
System.out.println(o2);
And the output is:
10
20

Related

Why do I get a ClassCastException using generics in this case?

This is the code: A simple ceneric class and trying to assign an integer to aa[0].
public class GenericTest<T> {
T [] aa = (T[]) new Object[2];
T bb;
public GenericTest(T x, T y) {
aa[0] = x; aa[1] = y;
System.out.println(aa[0] + " " + aa[1]); //OK
}
static public void main(String[] args) {
GenericTest<Integer> ll = new GenericTest<>(1,2);
ll.bb = 1; // OK
ll.aa[0] = 6; // ClassCastException from Object to Integer
}
}
In fact, the exception message is this:
java.lang.ClassCastException:
[Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
It is saying that it can't cast an Object[] to an Integer[].
The root cause of is the initializer in:
T [] aa = (T[]) new Object[2];
That typecast is an unsafe typecast. And indeed the compiler tells you that something is wrong:
$ javac GenericTest.java
Note: GenericTest.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Anyhow ... what is happening is that when you then do this:
ll.aa[0] = 6;
the JVM is trying to cast the ll.aa to an Integer[] ... because that is what the static typing says that it should be. But it isn't an Integer[]. It is an Object[]. Since Object[] is not assignment compatible with an Integer[] that gives you a class cast exception.
(Why is it doing a hidden type cast? Well this is how the JVM ensures runtime type safety in the face of possible unsafe casts and the like!)
How to fix it?
Avoid using T[]. Use List<T> instead.
Unfortunately, if you have to use T[] there is no easy fix. Basically arrays of a generic type parameter are difficult to create. You end up having to pass the Class object for the parameter's actual class as an extra parameter. Something like this:
import java.lang.reflect.Array;
public class GenericTest<T> {
T [] aa;
T bb;
public GenericTest(Class<T> cls, T x, T y) {sy
aa = (T[]) Array.newInstance(cls, 2);
aa[0] = x; aa[1] = y;
System.out.println(aa[0] + " " + aa[1]); //OK
}
static public void main(String[] args) {
GenericTest<Integer> ll = new GenericTest<>(Integer.class, 1, 2);
ll.bb = 1; // OK
ll.aa[0] = 6; // ClassCastException from Object to Integer
}
}
There is still a warning about an unsafe typecast ... but in this case it is safe to suppress the warning.
For Java 8 onwards, there is another solution which involves passing a reference to the array constructor for Integer[]; see Andy Turner's answer. This is cleaner than using reflection and calling Array.newInstance, but you still have to pass an extra parameter to the constructor.
This is what happens when you use generics. Because generics are erased at runtime, compiler still needs to somehow be safe (after erasure) that things work correctly. Let's simplify this:
GenericTest<Integer> ll = new GenericTest<>(1,2);
ll.bb = 1; // OK
System.out.println(ll.aa.getClass());
The last line is going to be translated to:
28: getfield #7 // Field aa:[Ljava/lang/Object;
31: checkcast #42 // class "[Ljava/lang/Integer;"
notice the checkcast. Since your T was resolved as Integer, means that the array must be Integer[] too; when in reality it is Object []. Compiler is trying to warn you btw when you do :
T [] aa = (T []) new Object[2];
because this is unsafe. In general, generic arrays are a major headache in java, imo.
This line:
ll.aa[0] = 6;
fails because the compiler has inserted some casts. Because it knows that ll is a GenericTest<Integer>, it expected aa to be an Integer[].
The compiler inserts a cast to this type before you can do anything with it: it is evaluated identically to:
((Integer[]) ll.aa)[0] = Integer.valueOf(6);
The problem is that ll.a isn't an Integer[], it's an Object[], so this cast fails.
These casts are inserted even when you're doing something that could be applied to "any array", or even "any object": for example, ll.a.toString() would have casts inserted, even though toString is available on all subclasses of Object, i.e. all objects.
T [] aa = (T[]) new Object[2];
As already pointed out by StephenC, this is an unchecked cast. Checked casts are instructions inserted into the bytecode, which statically check that the object is of a particular type. But, because T is a type variable, there is no "particular type" that the compiler can choose to insert a checkcast instruction on that assignment.
The correct way to deal with this is either:
Use a List<T> instead of a T[] as the field type. Generics and arrays don't play nicely together anyway, so you're better off sticking with generics.
You can use a fixed-length array, e.g. Arrays.asList(null, null), to mimick an array, insofar as you can only set the elements, not clear/add/remove etc.
Inject a T[] (or a Supplier<T[]>, or an IntFunction<T[]> etc) into the constructor of the GenericTest, in order that you push the burden of ensuring the correct type onto the caller:
public GenericTest(T x, T y, IntFunction<T[]> arrayFn) {
// ...
aa = arrayFn.apply(2); // Creates an array of the right length.
}
// Then...
GenericTest<Integer> ll = new GenericTest(1, 2, Integer[]::new);
ll.aa[0] = 6;
This will then be type-correct, because aa was created using Integer[]::new, i.e. it's an Integer[]. The casts are still inserted, they just pass the type check.
T [] aa = (T[]) new Object[2];
Because that line is broken. Your compiler warned you when you tried to compile it. As a general rule, if you get a compiler warning you do not understand, then do something else - java compiler warnings are best treated as: "Your code is completely broken and this is not going to work, but for the benefit of e.g. trying to start this application up because you want to test a completely different part of it, we'll just slap this broken stuff in a class file so you can move on, for now. Do not under any circumstances publish this to production until you fully understand this warning first."
In java, generics is a 100% compile-time show: Generics are compiler-checked documentation. At runtime, generics are mostly erased, and the few places where they remain, they are 'comments' as far as the runtime is concerned. The verifier and the runtime system never does any checks, ever. If javac allows it, the generics are now done being useful.
This in sharp contrast to arrays: Arrays are their own object and they DO know their 'component type'. Also, the compiler doesn't do all that much checking on them, but the runtime DOES. If you use some classfile hackery to put an Integer in a List<String>, the runtime will let you. However, you cannot use any amount of hackery to put an Integer in a String[]. It just won't work - you end up with an ArrayStoreException instead.
Because arrays actually know their type, at runtime, Integer[] and Object[] are different types (whereas at runtime, a List is just a List - the runtime doesn't know what generics are).
T is Integer, and that variable is of type T[], therefore, it is Integer[], therefore, any interactions with aa get you an implicit cast to Integer[]. This is 100% analogous to this code:
List<String> list = new ArrayList<String>();
List raw = list; // legal, but gets you a warning.
raw.add(5);
System.out.println(list.get(0));
You can compile it. If you run it, the last line throws ClassCastException which is weird, there is no cast there at all! Ah, but there is. Because the compiler rewrote that for you, into this code (and remember, the runtime does not know generics, which is why it is rewritten like this):
List list = new ArrayList();
List raw = list;
raw.add(Integer.valueOf(5));
String $v = (String) list.get(0);
System.out.println($v);
You'd think: Wait, why cast it? There is a System.out.println(Object) variant? But, that's just the rules. Java will immediately cast that because it can, after all, list is a List<String> which means its get(int idx) method can be assumed to always return strings, so java will cast it as soon as possible even if not needed.
The exact same thing happens in your code: It sees T[], where T is bound to Integer, so, aa is cast to Integer[] even if it wasn't needed. This cast fails at runtime because an object created as new Object[2] is clearly not an Integer[]. After all, I can invoke:
Integer[] x = ...;
System.out.println(x.getClass().getComponentType());
and this code is guaranteed to print java.lang.Integer. If I can write code so that java.lang.Object is printed, that would be broken, and yet that is exactly what would happen.
So how do I fix it?
Do not, ever, write T[]. It's as simple as that.
Arrays are low-level constructs you shouldn't be using, especially if the component type is non-primitive. If you MUST use them, then they should be relegated to internal implementation details, hidden behind a nicer API.
Do the casting in those wrappers.
This is precisely how ArrayList works. ArrayList has an array inside that holds your list elements (hence the name ArrayList). That is defined as Object[] storage = new Object[...];. Not T[] storage = (T[]) new Object[..]. Then, the get method of ArrayList does the casting:
public T get(int idx) {
// check if idx is within 0-size()...
return (T) storage[idx];
}
This cast does nothing (javac emits a warning that it has no idea what T might be and the runtime doesn't either, so nobody can check this), but this will all work at runtime (obviously: ArrayList works fine).
You need to do the same thing, and hide that array, if you must use it. Or better yet, don't do any of this stuff and use List<T> instead. Arrays of non-primitive types are a blight: If you use them, you better have an excellent reason for it, and hide it as much as possible.
This code works (printing the class inside the constructor....) and I don't know why
public class GenericTest<AnyType> {
public AnyType [] aa = (AnyType []) new Object[2];
public AnyType bb;
public GenericTest(AnyType x, AnyType y) {
aa[0]=x; aa[1]=y; System.out.println(aa.getClass());
System.out.println( aa[0]+" "+aa[1]); //OK
}
static public void main(String[] args) {
GenericTest<Integer> ll = new GenericTest<>(1,2);
ll.aa[0]= 6; // ClassCastException from Object to Integer
}
}

Generic list conversion to an array

Assuming that I have the following class
public class A <T>{
private T [] datas;
// more code here ...
}
And I desire to take advantage of the constructor to initialize the array. Suppose that I have the following constructor
public A(T element){....}
Java does not allow me to use something like
datas = new T[10]
And it will complain that I cannot create a generic array of T
But I can still use a work around like:
#SuppressWarnings("unchecked")
public A(T element){
List<T> datasList = new ArrayList<T>();
datasList.add(element);
datas =(T[]) datasList.toArray();
}
I have a warning from the compiler that's why I had to add the #SuppressWarnings, but my point is related to the following comment from the toArray method documentation (Please take a look at the picture)
It talks about the returned array being safe. So does that means it is safe to use this method? If not why? And what would be a better way to do such an initialisation in a constructor? I would like to also consider the case of a variable list of T elements in an overloaded constructor like
public A(T... elements){....}.
You can create an instance of a generic array using the following:
public A(T element){
int length = 10;
datas = (T[])Array.newInstance(element.getClass(), length);
}
However, there's a problem if element would be a subclass of T, e.g. if you'd call it like this:
A<Number> numberA = new A<>( Integer.valueOf(1) );
Here T would be Number but the class of element would be Integer.
To mitigate that you could pass a vararg array of type T, e.g. like this:
//firstElement only exists to force the caller to provide at least one element
//if you don't want this then just use the varargs array
A(T firstElement, T... furtherElements){
int length = 10;
Class<?> elementClass = furtherElements.getClass().getComponentType();
datas = (T[])Array.newInstance( elementClass, length);
}
Since varargs always result in an array (even of length 0) you'll get an array of type T and can get the component type of that.
So in the case above numberA.datas would be a Number[] array and not an Integer[] array.
You can pass generics, but you can't call new T (or new T[ ]).
Keep in mind that generics are gone after compilation, so it actually only helps when writing the code. Knowing it's gone during runtime, it's also obvious that new T( ) can't be called as generic, T is removed in runtime.
It's safe to do, because you create that list in full control, accepting only objects of your generic type.
A nicer way (imho) is to create a static method as it is purely input-->output. You have to declare your generics before the method return type:
public < T > T[ ] toArray(T... objects) { ... }

Generics: how to make the compiler control what type I put into a constructor

This example is taken from Thinking in Java.
public class Automobile {
}
public class Holder<T> {
private T a;
public Holder(T a){
this.a = a;
}
public void set(T a){
this.a = a;
}
public T get(){
return a;
}
public static void main(String[] args){
Holder<Automobile> h = new Holder<Automobile>(new Automobile());
Automobile a = (Automobile)h.get();
}
}
Then there goes explanation: you must specify what type you want to put into it using the same angle brackets syntax as you can see in main().
Well, I can't understand anything. I would understand that must word as a possible compile time error in case of violation of this rule.
But this works:
Holder<Automobile> h = new Holder(new Automobile());
Automobile a = h.get();
And this works:
Holder h = new Holder(new Automobile());
Automobile a = (Automobile)h.get();
So, as I can see, the compiler won't control what I put into the Holder object. Well, then I don't catch generics at all. And I have two questions:
What is the reason to use them? Only to save me some effort when casting object back to Automobile?
Is there any way to make the compiler control me so that I should really put Automobile into the Holder?
The casting here is unnecessary:
Holder<Automobile> h = new Holder<Automobile>(new Automobile());
Automobile a = (Automobile)h.get();
But here it is necessary:
Holder h = new Holder(new Automobile());
Automobile a = (Automobile)h.get();
And this is the best way of doing things ever since java 1.5 and above:
Holder<Automobile> h = new Holder<Automobile>(new Automobile()); //specify the generic parameter on both static and dynamic type
Automobile a = h.get(); //no casting is necessary
Or above java 1.7 for simplicity:
Holder<Automobile> h = new Holder<>(new Automobile()); //diamond operator so you don't need to respecify the same thing
Automobile a = h.get();
The reason why it's useful to use generics in this fashion is so that you can't do the following:
Integer a = new Integer(6);
List list = new ArrayList();
list.add(a);
list.add("5");
for(int i = 0; i < list.size(); i++)
{
Integer integer = (Integer)list.get(i); //crashes at "5" which is String at runtime
System.out.println(integer);
}
As you can see, if you can put any subclass of Object into the list without bounds, then you need explicit casting, and if you put anything that is not what you expect it to be into the List, then it will crash. Please note that without generics, you're also not told what type you are expected to place into the list, which means you need to keep track of what the List is supposed to contain, which is really crappy when you're trying to do logic like the following: Java generics and casting to a primitive type
And I'm not even sure if this is possible without generics: Is it possible to cast Map<Field, Value> to Map<Mirror, Mirror> when it is known that Field and Value extend Mirror?
So technically generics enable extra features while also encourages type safety and therefore error-less code, which is always nice.
Integer a = new Integer(6);
List<Integer> list = new ArrayList<Integer>();
list.add(a);
list.add("5"); //this will not compile -> you won't need to wait until runtime to see that things are incorrect!
The point of generics is to write datastructures which can contain or work with any object. You specify at use-time which types of objects it should contain, e.g.:
ArrayList<String> stringList = new ArrayList<String>();
(Diamondoperator for show not written in short)
to answer the questions:
1: to write code which can work with all objects (like the collections), and yes, the compiler understands and can type-check your code at compiletime
2: yeah, dont use generics and instead write the code spezific for your automobiles

Java Array declaration like { A[] aa = new B[10]}

Today I've written simple test about syntax of declaring array, so, there are 3 classes:
public class A {
}
public class B extends A {
}
public class C extends A {
}
and I've tried to create array using next syntax
A[] aa = new B[10];
So, It is possible, but we can added just instances of class B to this array , If you will try to add instances of A or C you receive java.lang.ArrayStoreException and question, Why can we create array using syntax like that and where is it can be used and make some profit ?
Thanks.
The array holds type Bs. Even though the reference aa could hold arrays of type A[], B[] or C[], (since C and B both extend A), at runtime, the array can only hold things which are Bs.
Class A is not a B. Class C is not a B. Hence the runtime exception.
EDIT:
There are many potential uses for code like this. For example, You can declare an array like this because you might not know until runtime the more explicit type you are working with.
For instance:
Animal[] animals;
if (useDogs) {
animals = new Dog[num];
} else {
animals = new Cat[num];
}
loadIntoCar(animals);
The reason this syntax is allowed in the language is that sometimes you don't care what subclass of objects are in the array. The Java Language Specification rules for array subtyping include:
If S and T are both reference types, then S[] >1 T[] iff S >1 T.
(where >1 means "direct subtype").
This allows one to write a method like this:
public void printArray(Object[] array) {
System.out.print('[');
boolean first = true;
for (Object obj : array) {
if (!first) {
System.out.print(", ");
} else {
first = false;
}
System.out.print(String.valueOf(obj));
}
System.out.print(']');
}
and then call it with:
String[] foo = { "a", "b", "c" };
printArray(foo);
This language feature does have the unfortunate effect of deferring ArrayStoreException problems to run time.
Arrays types are covariant, which means you can declare A[] a = new B[] (Collections, on the contrary, are not : you cannot declare List<A> a = new List<B>()).
This is why the compiler cannot preemptively check that you will only put elements of a valid type in the array, which explains why the check happens only at runtime, when you try to insert an element into the array.
If you want examples, take a look at Arrays. If Java didn't allow arrays polymorphism, we couldn't have functionalities such as sort(Object[] a), toString(Object[] a), binarySearch(Object[] a, Object key), etc. Remember that Band C are subtypes of Object, so you can use that functionality for all of them. Take note that arrays predate generics (Java 1.5.0) and that even generics have it's flaws (the trade-off for Java is that you can't know the generic type at runtime because of type-erasure).

What's the reason I can't create generic array types in Java?

What's the reason why Java doesn't allow us to do
private T[] elements = new T[initialCapacity];
I could understand .NET didn't allow us to do that, as in .NET you have value types that at run-time can have different sizes, but in Java all kinds of T will be object references, thus having the same size (correct me if I'm wrong).
What is the reason?
It's because Java's arrays (unlike generics) contain, at runtime, information about its component type. So you must know the component type when you create the array. Since you don't know what T is at runtime, you can't create the array.
Quote:
Arrays of generic types are not
allowed because they're not sound. The
problem is due to the interaction of
Java arrays, which are not statically
sound but are dynamically checked,
with generics, which are statically
sound and not dynamically checked.
Here is how you could exploit the
loophole:
class Box<T> {
final T x;
Box(T x) {
this.x = x;
}
}
class Loophole {
public static void main(String[] args) {
Box<String>[] bsa = new Box<String>[3];
Object[] oa = bsa;
oa[0] = new Box<Integer>(3); // error not caught by array store check
String s = bsa[0].x; // BOOM!
}
}
We had proposed to resolve this
problem using statically safe arrays
(aka Variance) bute that was rejected
for Tiger.
-- gafter
(I believe it is Neal Gafter, but am not sure)
See it in context here: http://forums.sun.com/thread.jspa?threadID=457033&forumID=316
By failing to provide a decent solution, you just end up with something worse IMHO.
The common work around is as follows.
T[] ts = new T[n];
is replaced with (assuming T extends Object and not another class)
T[] ts = (T[]) new Object[n];
I prefer the first example, however more academic types seem to prefer the second, or just prefer not to think about it.
Most of the examples of why you can't just use an Object[] equally apply to List or Collection (which are supported), so I see them as very poor arguments.
Note: this is one of the reasons the Collections library itself doesn't compile without warnings. If this use-case cannot be supported without warnings, something is fundamentally broken with the generics model IMHO.
The reason this is impossible is that Java implements its Generics purely on the compiler level, and there is only one class file generated for each class.
This is called Type Erasure.
At runtime, the compiled class needs to handle all of its uses with the same bytecode. So, new T[capacity] would have absolutely no idea what type needs to be instantiated.
The answer was already given but if you already have an Instance of T then you can do this:
T t; //Assuming you already have this object instantiated or given by parameter.
int length;
T[] ts = (T[]) Array.newInstance(t.getClass(), length);
Hope, I could Help,
Ferdi265
The main reason is due to the fact that arrays in Java are covariant.
There's a good overview here.
I like the answer indirectly given
by Gafter. However, I propose it is wrong. I changed Gafter's code a little. It compiles and it runs for a while then it bombs where Gafter predicted it would
class Box<T> {
final T x;
Box(T x) {
this.x = x;
}
}
class Loophole {
public static <T> T[] array(final T... values) {
return (values);
}
public static void main(String[] args) {
Box<String> a = new Box("Hello");
Box<String> b = new Box("World");
Box<String> c = new Box("!!!!!!!!!!!");
Box<String>[] bsa = array(a, b, c);
System.out.println("I created an array of generics.");
Object[] oa = bsa;
oa[0] = new Box<Integer>(3);
System.out.println("error not caught by array store check");
try {
String s = bsa[0].x;
} catch (ClassCastException cause) {
System.out.println("BOOM!");
cause.printStackTrace();
}
}
}
The output is
I created an array of generics.
error not caught by array store check
BOOM!
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at Loophole.main(Box.java:26)
So it appears to me you can create generic array types in java. Did I misunderstand the question?
From Oracle tutorial:
You cannot create arrays of parameterized types. For example, the following code does not compile:
List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile-time error
The following code illustrates what happens when different types are inserted into an array:
Object[] strings = new String[2];
strings[0] = "hi"; // OK
strings[1] = 100; // An ArrayStoreException is thrown.
If you try the same thing with a generic list, there would be a problem:
Object[] stringLists = new List<String>[]; // compiler error, but pretend it's allowed
stringLists[0] = new ArrayList<String>(); // OK
stringLists[1] = new ArrayList<Integer>(); // An ArrayStoreException should be thrown,
// but the runtime can't detect it.
If arrays of parameterized lists were allowed, the previous code would fail to throw the desired ArrayStoreException.
To me, it sounds very weak. I think that anybody with a sufficient understanding of generics, would be perfectly fine, and even expect, that the ArrayStoredException is not thrown in such case.
In my case, I simply wanted an array of stacks, something like this:
Stack<SomeType>[] stacks = new Stack<SomeType>[2];
Since this was not possible, I used the following as a workaround:
Created a non-generic wrapper class around Stack (say MyStack)
MyStack[] stacks = new MyStack[2] worked perfectly well
Ugly, but Java is happy.
Note: as mentioned by BrainSlugs83 in the comment to the question, it is totally possible to have arrays of generics in .NET
class can declare an array of type T[], but it cannot directly instantiate such an array. Instead, a common approach is to instantiate an array of type Object[], and then make a narrowing cast to type T[], as shown in the following:
public class Portfolio<T> {
T[] data;
public Portfolio(int capacity) {
data = new T[capacity]; // illegal; compiler error
data = (T[]) new Object[capacity]; // legal, but compiler warning
}
public T get(int index) { return data[index]; }
public void set(int index, T element) { data[index] = element; }
}
It is because generics were added on to java after they made it, so its kinda clunky because the original makers of java thought that when making an array the type would be specified in the making of it. So that does not work with generics so you have to do
E[] array=(E[]) new Object[15];
This compiles but it gives a warning.
There surely must be a good way around it (maybe using reflection), because it seems to me that that's exactly what ArrayList.toArray(T[] a) does. I quote:
public <T> T[] toArray(T[] a)
Returns an array containing all of the
elements in this list in the correct order; the runtime type of the
returned array is that of the specified array. If the list fits in the
specified array, it is returned therein. Otherwise, a new array is
allocated with the runtime type of the specified array and the size of
this list.
So one way around it would be to use this function i.e. create an ArrayList of the objects you want in the array, then use toArray(T[] a) to create the actual array. It wouldn't be speedy, but you didn't mention your requirements.
So does anyone know how toArray(T[] a) is implemented?
If we cannot instantiate generic arrays, why does the language have generic array types? What's the point of having a type without objects?
The only reason I can think of, is varargs - foo(T...). Otherwise they could have completely scrubbed generic array types. (Well, they didn't really have to use array for varargs, since varargs didn't exist before 1.5. That's probably another mistake.)
So it is a lie, you can instantiate generic arrays, through varargs!
Of course, the problems with generic arrays are still real, e.g.
static <T> T[] foo(T... args){
return args;
}
static <T> T[] foo2(T a1, T a2){
return foo(a1, a2);
}
public static void main(String[] args){
String[] x2 = foo2("a", "b"); // heap pollution!
}
We can use this example to actually demonstrate the danger of generic array.
On the other hand, we've been using generic varargs for a decade, and the sky is not falling yet. So we can argue that the problems are being exaggerated; it is not a big deal. If explicit generic array creation is allowed, we'll have bugs here and there; but we've been used to the problems of erasure, and we can live with it.
And we can point to foo2 to refute the claim that the spec keeps us from the problems that they claim to keep us from. If Sun had more time and resources for 1.5, I believe they could have reached a more satisfying resolution.
As others already mentioned, you can of course create via some tricks.
But it's not recommended.
Because the type erasure and more importantly the covariance in array which just allows a subtype array can be assigned to a supertype array, which forces you to use explicit type cast when trying to get the value back causing run-time ClassCastException which is one of the main objectives that generics try to eliminate: Stronger type checks at compile time.
Object[] stringArray = { "hi", "me" };
stringArray[1] = 1;
String aString = (String) stringArray[1]; // boom! the TypeCastException
A more direct example can found in Effective Java: Item 25.
covariance: an array of type S[] is a subtype of T[] if S is a subtype of T
T vals[]; // OK
But, you cannot instantiate an array of T
// vals = new T[10]; // can't create an array of T
The reason you can’t create an array of T is that there is no way for the
compiler to know what type of array to actually create.
Try this:
List<?>[] arrayOfLists = new List<?>[4];

Categories