1

Although this code compiles successfully, it throws a class cast exception error at run-time:

class Test {
    public static void main(String[] args) {
        MyArray<String> x = new MyArray<>();
        x.a[0] = "test string";
    }
}

class MyArray<T> {
    T[] a;

    MyArray() {
        this.a = (T[]) new Object[1];
    }
}

The error message:

Exception in thread "main" java.lang.ClassCastException:
[Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
    at Test.main(Test.java:4)

One possible, yet simple, solution I know of is to declare the array as an array of type Object and then just cast an array element back to T when you want to retrieve it from the array, like this:

class Test {
    public static void main(String[] args) {
        MyArray<String> x = new MyArray<>();
        x.a[0] = "test string";
        System.out.println(x.get(0));
    }
}

class MyArray<T> {
    Object[] a;

    MyArray() {
        this.a = new Object[1];
    }

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

This works just fine. But the problem with this approach though is that I can now put absolutely anything in the array and yet keep it there safe and sound until such time as I try to pull it out of the array which will give me a big, fat class cast exception error!

    MyArray<String> x = new MyArray<>();
    x.a[0] = 34;
    System.out.println(x.get(0)); // run-time error

This is what we'd get if we ran this piece of code:

 Exception in thread "main" java.lang.ClassCastException: 
 java.lang.Integer cannot be cast to java.lang.String
    at Test.main(Test.java:5)

Obviously, it's a lot better when all your array elements are of the same type as opposed to being generic objects of type Object. There's gotta be a way to do that.

By the way, the example code I presented here has been significantly simplified. I know that I can use set and get methods that can do the necessary type casting which, of course, will help make it completely safe to add and get elements in and out of the array. The problem with this is that if I have lots of other methods in the class that also do a lot of casting, the tedium of writing all those cast operators becomes a real pain. I thought it'd be really nice if I could just store all the elements in the array as a certain type from the word go. This would simplify the coding process and make code much cleaner and easier to read. So, any words of wisdom you wanna pass my way are most welcome and going to be much appreciated.

3 Answers3

5

But the problem with this approach though is that I can put absolutely anything in the array

Yes. That's why you don't expose the array:

class MyArray<T> {
    private Object[] a;

    MyArray() {
        this.a = new Object[1];
    }

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

    void set(int index, T value) {
        a[index] = value;
    }
}

and set() prevents you from doing the wrong thing.

As an array of size 1 won't be very useful, I modify the thing here:

class MyArray<T> {
    private Object[] a;

    MyArray(int size) {
        this.a = new Object[size];
    }

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

    void set(int index, T value) {
        a[index] = value;
    }
}
glglgl
  • 81,640
  • 11
  • 130
  • 202
1

The ClassCastException happens due to type erasure. Basically what it comes down to is the fact that generics are syntactic sugar that happens entirely in the compiler. Since T could be anything the runtime type of the array must be Object, since that can store anything.

One way to solve this issue is to pass the class to the constructor so it can use it to create an array with the correct runtime type:

import java.lang.reflect.Array;

class MyArray<T> {
    T[] a;

    MyArray(Class<T> type) {
        this.a = (T[]) Array.newInstance(type, 1);
    }

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

You would use it thus:

MyArray<String> myStringArray = new MyArray<>(String.class);

Not ideal, but I think the least inelegant way to solve it.

Pepijn Schmitz
  • 1,916
  • 1
  • 15
  • 15
  • How do I pass "type" in? Could you please write example code that shows how to do that? –  Jun 11 '15 at 11:10
  • I edited my answer to add a usage example. – Pepijn Schmitz Jun 11 '15 at 11:15
  • That's just what the doctor ordered! Whether this is an ideal solution or not is an open question, or course. But the fact of the matter is that there's definitely no such thing as a free lunch. Thanks a lot. –  Jun 11 '15 at 11:55
  • Glad to hear it! Feel free to accept my answer... ;) – Pepijn Schmitz Jun 11 '15 at 12:23
0

You can use Object array and make it private. You would need to make a 'set' method too so you can't put anything. That is how an ArrayList work.

Other solution is to create the array of T with the reflection Array class. To do so you have to pass the actual type class as parameter:

class Test {
    public static void main(String[] args) {
        MyArray<String> x = new MyArray<>(String.class);
        x.a[0] = "test string"; // works
        x.a[1] = 1; // does not even compile
    }
}

class MyArray<T> {
    T[] a;

    MyArray(Class<T> clazz) {
        this.a = (T[]) Array.newInstance(clazz, 2);
    }
}
aalku
  • 2,720
  • 1
  • 18
  • 41