7

I've encountered a problem while doing a homework for online algorithms class. Casting Object[] to T[] where T is Comparable raises a run-time exception

public  static <T extends Comparable<? super T>> void mergeSort(T[] xs) {
    T[] aux = (T[]) new Object[xs.length];
    mergeSort(xs, aux, 0, xs.length);
}

I could rewrite it in C# which doesn't have any problems with creating generic arrays but I'd rather learn how to deal with this in Java.

synapse
  • 5,123
  • 6
  • 27
  • 56
  • I think you should see http://stackoverflow.com/questions/1817524/generic-arrays-in-java it is quite interesting. – Menelaos Apr 12 '13 at 21:45

3 Answers3

12

If you're getting a runtime exception, it means that the objects you tried to cast don't acutally have that type. Language doesn't have anything to do with it. There's probably a bug in your code.

Edit: It sounds like you are confused by how Java's type system works. In C#, generics actually represent different types at runtime. In Java, generic types don't exist at runtime. They are only a convenience to enable better compile time type checking. During compilation, generics are replaced by a real type in a process known as type erasure.

Normally, the erasure of a generic type is Object, but since you provided an upper bound for T, it is converted to that bound, Comparable. Therefore, after erasure, your code looks like this.

Comparable[] aux = (Comparable[]) new Object[xs.length];

In otherwords, you're creating an array of type Object[] and immediately trying to cast it to type Comparable[]. Since Object doesn't implement Comparable, the types are incompatible, so you get a runtime exception. You can fix this by creating an array of Comparables instead.

public  static <T extends Comparable<? super T>> void mergeSort(T[] xs) {
    T[] aux = (T[]) new Comparable[xs.length];
    mergeSort(xs, aux, 0, xs.length);
}
Antimony
  • 33,711
  • 9
  • 88
  • 96
  • Obviously there's a bug and obviously it has something to do with the way Java handles generics. In C# there's no problem with creating arrays of constrained type parameter. – synapse Apr 12 '13 at 21:49
  • @synapse I've edited my answer to hopefully clarify things for you. – Antimony Apr 12 '13 at 22:36
3

Try this:

public  static <T extends Comparable<? super T>> void mergeSort(T[] xs) {
    T[] aux = (T[])java.lang.reflect.Array.newInstance(xs.getClass().getComponentType(), xs.length);
    mergeSort(xs, aux, 0, xs.length);
}
jdb
  • 4,140
  • 17
  • 21
  • There's a much simpler way to do this. See my answer. – Antimony Apr 12 '13 at 22:38
  • Simpler but dangerous. You can also just change the type like this: > and it will compile and run even with the original code (depends on the signature of the mergeSort method). But for example if T is always String and you try to do String[] aux2 = (String[]) aux you will get an exception. Your Comparable array has the same problem. So it is simpler but it may not work all the time. – jdb Apr 12 '13 at 23:40
  • @Jdb That's not how generics work. Even if `T` is always `String`, the actual cast it will do is still `(Comparable[])` because that's the only bound the compiler cares about (specifically, it's the leftmost bound, so it'd be `Object[]` in your example). I don't know of any way you could get a runtime exception from pure Java code using this unless you have an unsafe cast somewhere. – Antimony Apr 13 '13 at 00:09
  • It's theoretically possible that the child `mergeSort` is doing something weird which could cause an exception, but there's no way to know. Based only on the code posted, my suggestion is safe at any rate. The main danger is if the created array is returned to something that knows the actual value of `T`, but that's not happening here. – Antimony Apr 13 '13 at 00:15
2

Arrays are covariant and this means that they retain the type of their elements at runtime. Java's generics are not. So basically they don't mix.

See also: Generic arrays in Java

You cannot create arrays of generics and you cannot cast to them. Better to use arraylists.

Community
  • 1
  • 1
Menelaos
  • 20,773
  • 14
  • 71
  • 130