5

Here we have generic method:

public static <T extends Comparable<T>> T[] function(T[] a)
{
    Object[] m = new Object[2];

    /* some work here */

    return (T[]) m;
}

A ClassCastException is thrown . What's wrong with it?

Heisenbug
  • 37,414
  • 27
  • 126
  • 181
Majid Azimi
  • 5,009
  • 12
  • 56
  • 100

6 Answers6

4

An array of Object is not an array of any subclass of Object. You are facing one of the limitations of generics in Java: you cannot create a generic array. See this thread for an approach that does work.

Community
  • 1
  • 1
Ted Hopp
  • 222,293
  • 47
  • 371
  • 489
  • But we can write such code: `E[] array = (E[]) new Object[10]; //Although is gives warning` It is the same as in the code, isn't it? – Majid Azimi Jun 30 '11 at 14:27
  • If you compile that with `-Xlint:unchecked` you'll get a warning. And you won't be happy with the results at run time unless `E` is `Object`. – Ted Hopp Jun 30 '11 at 14:29
3

Object is not Comparable. You have to define your array to be of a comparable type.

Since you are passing an array, you can perhaps use the array's type:

T[] m = Array.newInstance(a.getClass().getComponentType(), 2);

And of course, you will have to put Comparable objects inside.

Bozho
  • 554,002
  • 136
  • 1,025
  • 1,121
  • So what is deference? `Array.newInstance` is creating new array and returning the `Object[]`, then we cast it to `T[]`. This is what i did in the code. – Majid Azimi Jun 30 '11 at 19:59
  • 1
    The important part is to put comparable objects inside the array. The newInstance just saves some casting. – Bozho Jun 30 '11 at 20:11
  • 1
    @Majid Array.newInstance as used here will create a new array of the same class as the input. In Java there is a difference between arrays with different component types, Object[] and Comparable[] are two different classes. It's easy to see this if you compare the result of getClass on two arrays of different types. As Object is not of the same type as your generic T (and can never be) you will always get a ClassCastException. – Andreas Holstenson Jun 30 '11 at 20:22
  • Object is super class of all classes. why we cannot assign arrays of different classes to it? Another question is why `E[] e = (E[]) new Object[3]` works, But `Object[] o = new Object[3]` and then `E[] e = (E[]) o` doesn't work? They are doing the same? aren't they? – Majid Azimi Jul 01 '11 at 08:27
3

You have two different problems here.

First, just erase the generics and look at the code without generics (this is what it gets erased to at compile-time):

public static Comparable[] function(Comparable[] a)
{
    Object[] m = new Object[2];

    /* some work here */

    return (Comparable[]) m;
}

You can't cast an object whose actual, runtime class Object[] to Comparable[]. Period.

Second, even if you re-wrote your code to create a Comparable[] instead of Object[]

public static <T extends Comparable<T>> T[] function(T[] a)
{
    Comparable[] m = new Comparable[2];

    /* some work here */

    return (T[]) m;
}

it would still not work. It won't throw a ClassCastException inside this function. But it will throw it in any code that calls this function. For example,

String[] foo = function(new String[0]);

will throw a ClassCastException, because when you erase it, you see that the compiler places a cast for the thing that comes out of the generic method:

String[] foo = (String[])function(new String[0]);

and you can't cast an object whose actual class is Comparable[] to String[].


When you ask "what is the difference" to people who have said that Array.newInstance() is the way to create an array of a class known at runtime. The difference is that the object returned by Array.newInstance() has an ''actual, runtime'' type of Whatever[], where "Whatever" is the class of the class object passed to it. It doesn't matter that the static (compile-time type of the value) type is Object[]; it's the actual runtime type that matters.


When you say "Another question is why E[] e = (E[]) new Object[3] works", you are probably missing several points here. First of all, that only works if E is declared as <E> or <E extends Object>, i.e. E's lower bound is Object. And second, that is basically a lie (that's convenient in several places, like implementing a generic collection; but you have to understand why it's dangerous); and formally, you ''shouldn't'' be able to cast from an object whose actual type is Object[] to E[] when E is not Object. It only "works" because within the scope of E, E is erased, and so we can't check the cast. But if you try to return that object as an E[] to the outside world, you will get a ClassCastException in the same way.

newacct
  • 110,405
  • 27
  • 152
  • 217
2

Well, really you cannot cast an array of objects to an array of Comparables. It doesn't make any sense. Why would the compiler allow that? What's more, for example, the Integer class implements Comparable, but you cannot cast a Comparable array to an Integer array.

Luciano
  • 8,162
  • 5
  • 29
  • 56
0

T extends Comparable means that method parameter (in this case T) should extend from comparable. So when you try to do the following cast

(T[]) m;

You are trying to cast an Object[] to Comparable[] (or anything that extends Comparable).

Alfredo Osorio
  • 10,547
  • 10
  • 49
  • 79
0

This is what you want to be doing:

public static <T extends Comparable<T>> T[] function(final T[] a) {
    final T[] m = (T[]) Array.newInstance(a.getClass().getComponentType(), 2);

    /* some work here */

    return m;
}
Reverend Gonzo
  • 36,174
  • 6
  • 55
  • 77