8

Here is the code I'm using

public class aClass<T> {

    private T[] elements;

    public aClass(T[] elements) {
        this.elements = elements;
    }

    public void doSomething() {
        T[] newArray = (T[]) new Object[5];
        ...
    }

}

I've seen people saying that creating an array like this is a bad idea, due to it being not type safe. However, every time I use it, I have no problems with it. When would creating an array like this cause a problem?

Thanks

Stripies
  • 1,239
  • 5
  • 19
  • 28
  • 3
    See this [answer](http://stackoverflow.com/a/3866251/829571) to a related question and [this discussion](http://stackoverflow.com/questions/529085/java-how-to-generic-array-creation) too. – assylias Apr 05 '12 at 15:50

7 Answers7

4

You cannot create an array of T because Java does not know, at run time what is the type of T. This is due to the fact that in Java generics is implemented with type erasure. This means that the compiler discards most of the generic type information after ensuring everything is Ok.

With arrays the story is different, because Java needs to know the exact type of T in order to create the given array, and since such thing cannot be determined you cannot create an array of a generic type.

What you can do is to provide a instance of the actual array that you want to use, and the Java compiler can ensure it is of the appropriate type:

public static <T> void fillWith(T[] destiny, List<? extends T> source){
    for(int i=0; i<= destiny.length; i++){
        destiny[i] = source.get(i);
    }
}

The java.utils.Arrays.copy method offers an alternative carefully using generics and reflections that you can use as a reference for what you want to do.

public static <T> T[] copyOf(T[] original, int newLength) {
   return (T[]) copyOf(original, newLength, original.getClass());
}


public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
}
Edwin Dalorzo
  • 70,022
  • 25
  • 131
  • 191
4

Here is an example that causes issues:

public class Main {

    public static class List<E extends Number> {

        private E[] myE;

        public void hello(E e) {
            E[] newArray = (E[]) new Object[5];
            for (int i = 0; i < newArray.length; i++) {
                newArray[i] = e;
            }
            myE = newArray;
        }

    }

    public static <T> T[] createArray(int size) {
        return (T[]) new Object[size];
    }

    public static void main(String[] args) throws IOException {
        List<Integer> integers = new List<Integer>();
        integers.hello(5);
    }
}

Your code works because when you declare your generic parameter <T> it is unbound, meaning that it extends Object. When you cast your array to (T[])you are actually casting to (Object[]) because that is the best the compiler can do. Now, if you keep your array inside your code, you should not have too many problems. But if somebody outside your code can retrieve that array and has instantiated your class with a type other than object, he will have ClassCastException.

Guillaume Polet
  • 45,783
  • 4
  • 79
  • 115
2

Something that is not type safe doesn't create problems in itself. But it can hide problems at compile time that won't popup until the right moment.

You would be able to fully work in a not type safe environment without having problems, but it's a bad habit just because a type safe environment does guarantee you that you won't have a set of common runtime errors, you don't have any at the moment but you don't have any guarantee, and these are really important, any safety you can trust into means less effort.

Jack
  • 125,196
  • 27
  • 216
  • 324
1

The other people are wrong. There is no other way to create the array unless you have an instance of the type at creation time. See also How to create a generic array in Java?

If you have the type instance (= something that has the type Class<T>), you can call java.lang.reflect.Array.newInstance(Class<?>, int) but even then, you'd need the cast, so why bother?

I'd say things were different if the type of elements was Object instead of T but since that's not the case, the code is perfectly OK.

If you want to hide this further, you can write a helper method:

  @SuppressWarnings( "unchecked" )
  public static <T> T[] createArray( int size ) {
      return (T[]) new Object[size];
  }

That creates an array without needing a cast when you call it.

Community
  • 1
  • 1
Aaron Digulla
  • 297,790
  • 101
  • 558
  • 777
  • how come then I get a ClassCastException when I write the following? `public static class A { } public static T[] createArray(int size) { return (T[]) new Object[size]; } public static void main(String[] args) { A[] a = createArray(3); }` – Guillaume Polet Apr 05 '12 at 16:02
  • @Aaron I do not see how this could be possible, your code compiles fine, but would not run, it cause a `ClassCastException` at runtime because an array of `Object[]` is not an array of `T[]`. This downcast is not possible. – Edwin Dalorzo Apr 05 '12 at 16:03
1

It's safer to use a Collection in this cases. Just make something like

...
Collection<T> newElements = new ArrayList<T>(5);
...

Anyway, from what I known creating a generic array like this won't give you real problems, but "smells bad", as it requires explicit type casting. Do you really need an array in this case?

Caesar Ralf
  • 2,172
  • 1
  • 16
  • 35
1

I've seen people saying that creating an array like this is a bad idea, due to it being not type safe. However, every time I use it, I have no problems with it. When would creating an array like this cause a problem?

People say it is a bad idea because, logically, it is not correct to cast an object whose runtime type is Object[] to type T[] if T is not Object.

However, you do not see any immediate problems because, inside the class (within the scope of T), T is erased. So the cast from Object[] to T[] is not checked.

If all you ever do is use this variable inside your class (inside the scope of the type variable T), and you never return it to people outside of the class and there is no way people outside of the class can get access to it, then it will not cause any problems.

In fact, there are benefits to the way you're doing it -- you get all the benefits of generic type checking when getting and setting elements of the array, which you would not get if you simply followed the completely "type-safe" solution of using a variable of type Object[], in which case you would have to manually cast things you get out of it.

You will get a problem if you ever return this array variable to the outside (or otherwise allow the outside to access it) as type T[], because the calling code will expect a certain type of array, and the generics will insert a cast in the calling code when it receives this array, and the cast will fail at runtime (since e.g. a Object[] is not a String[]; they are different types at runtime). Here is a simple example:

public class aClass<T> {

    private T[] elements;

    public aClass(T[] elements) {
        this.elements = elements;
    }

    public void doSomething() {
        elements = (T[]) new Object[5];
        ...
    }

    public T[] getArray() {
        return elements;
    }

}

// some other code...
aClass<String> foo = new aClass<String>(new String[2]);
foo.doSomething();
String[] bar = foo.getArray(); // ClassCastException here
newacct
  • 110,405
  • 27
  • 152
  • 217
  • in your example, what does the cast `(T[])` allow you to do more than if you add used an `Object[]`? – Guillaume Polet Apr 06 '12 at 08:37
  • I meant assign to `elements`, sorry – newacct Apr 06 '12 at 18:40
  • The point is that the variable `elements` is type `T[]` instead of `Object[]`, which allows you to get and put elements into it of type `T` and get generics type checking at compile time – newacct Apr 06 '12 at 18:41
  • I am sorry and I don't want to look like I want to debate but I don't understand which methods you can reach by casting to (T[]) or what more you can if you would have cast to the upper bound of `T`. At best you know the upper bound of T and you instantiate and cast to that type (if `T extends Number` you can use `new Number[5]` without casting and access all methods of `Number`. I am really trying to see if I am missing on something. – Guillaume Polet Apr 06 '12 at 21:15
  • You are right, there wouldn't be any more methods you can access. The difference I was talking about is generic type checking. For example when you get things out of the array, it would be type `T` instead of the type of the upper bound (which you would have to cast to `T` in order to store in a variable of type `T` or return it as type `T`); or when you put things into it, it would check that they are type `T` instead of allowing everything. So it's just a little more convenient – newacct Apr 07 '12 at 00:14
0

You may have problems with comparissions and printing.. basically any time that knowing the type is important for formatting. the way you have it the system have no clue what data is in the array

RyanS
  • 3,455
  • 1
  • 21
  • 36
  • -1 this is not correct. By virtue of generics, to instantiate this class you provide a concrete type: `AClass employees = new AClass(new Employee[5]);` therefore specific methods (like comparing) will act on the specific type implementation (e.g. `employee.equals(...)`). – maasg Apr 05 '12 at 16:12
  • I disagree, he MAY still encounter these issues. Perhaps this is not fully correct? – RyanS Apr 05 '12 at 17:30