3

Why the following is possible in Java ?

Integer[] ints = (Integer[])new Comparable[10];

But it gets ClassCastException at runtime. What is the usecase to new an array of an interface.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
user2018791
  • 1,143
  • 15
  • 29

6 Answers6

6

To answer the specific question:

Comparable toComplare[] = new Comparable[10];

Why not create an array that will allow you to store any object that implements the Comparable interface?!

The point is: the interface denotes a "common functionality" - and it could be helpful to only look at objects from that "view".

Of course, the objects stored in that array are always of some "real" class - but all these objects will implement the functionality that the Comparable interface provides.

So you could do things like:

toCompare[0] = new Integer(5);
toCompare[1] = new BigDecimal("3.2");
...

I am not saying that this is something that you would use frequently, but as said - it allows you to "collect" objects under a certain, specific "view" of their capabilities. It is also worth pointing out: having such an array does not mean that you would be able to do:

toCompare[0].compareTo(toCompare[1]);

successfully!

Beyond that: a cast always implies somehow that you, the programmer know something the compiler doesn't know. So the compiler steps back and lets you do that - assuming you know what you are doing. But as the code you are showing in the question is obviously not correct, reality comes back biting you at runtime. And yes, it would be possible to decide at compile that the given code is incorrect.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • In this particular case, a static analyzer would probably also flag it at compile-time. – Thilo Aug 10 '17 at 10:27
  • @Thilo Correct; I added another sentence about that. The fact that the compiler doesn't complain here is probably the most interesting aspect of this question. – GhostCat Aug 10 '17 at 10:29
  • 1
    The compiler cannot reject it as per language spec. (But it might issue a warning, we've seen more and more of these being added over the years) – Thilo Aug 10 '17 at 10:31
  • @Thilo Can you point to the specific section in the JLS you have in mind here? – GhostCat Aug 10 '17 at 10:33
  • Still looking for it. For now, related thread: https://stackoverflow.com/questions/10388136/java-casting-resulting-in-run-time-error-instead-of-compilation-error https://stackoverflow.com/questions/14467999/cast-a-superclass-to-a-subclass?noredirect=1&lq=1 – Thilo Aug 10 '17 at 10:38
  • FWIW, they could add new static type check errors every time they define a new language version. So a `-target 9` could include it. Similar to how we now have "effectively final" variables we did not have before (those lead to code being compiled that did not compile before, though, rather the opposite of what we have here) – Thilo Aug 10 '17 at 10:43
  • Also somewhat related: Unreachable code *error* vs Dead code *warning*: https://stackoverflow.com/questions/2141029/unreachable-code-error-vs-dead-code-warning-in-java-under-eclipse – Thilo Aug 10 '17 at 10:44
  • Probably Section 5.5.1: "If S is an array type SC[], that is, an array of components of type SC: [....] If T is an array type TC[], that is, an array of components of type TC, then a compile-time error occurs unless one of the following is true: [...] TC and SC are reference types and type SC can undergo casting conversion to TC." Which means, as long as `Comparable` can be cast to `Integer`, the same can be done of arrays of those. – Thilo Aug 10 '17 at 10:52
1

Regard this case: You have an interface and two (or more) classes that implement that interface:

interface MyInterface
{
    public void someMethod();
}

class MyClass1 implements MyInterface
{
    public void someMethod() { System.out.println("foo");}
}

class MyClass2 implements MyInterface
{
    public void someMethod() { System.out.println("bar");}
}

And you call it like this:

public static void main(String[] args)
{
    MyInterface[] array = new MyInterface[2];
    array[0] = new MyClass1();
    array[1] = new MyClass2();

    array[0].someMethod();
    array[1].someMethod();
}

An array of an interface gives you the method of holding different implementations of that interface in an array

ParkerHalo
  • 4,341
  • 9
  • 29
  • 51
1

Having Integer implement Comparable, doesn't mean Integer[] implements Comparable[], so you can't convert arrays of different type. You can, however, put Integer in element of Comparable[] array.

partlov
  • 13,789
  • 6
  • 63
  • 82
  • This is wrong. You can absolutely assign an `Integer[]` to `Comparable[]`. This compiles and runs just fine: `Comparable[] x = new Integer[10];` – Thilo Aug 10 '17 at 13:20
  • This is completely opposite what I wrote. Try this `Integer[] ints = (Integer[]) new Comparable[] { 1 };`, and you will receive `Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Comparable; cannot be cast to [Ljava.lang.Integer;` – partlov Aug 10 '17 at 14:51
  • You wrote "`Integer[]` does not implement `Comparable[]`". That is false. Every `Integer[]` is also a `Comparable[]`. – Thilo Aug 11 '17 at 06:38
1

The compiler looks at the type of the right side, and sees that it is an array of Comparable. In general, it could be an Integer[] (because that is assignable to Comparable[]).

We know that it will not be an Integer[], because that right-hand expression is a constructor call. But the compiler does not look that far. It uses the same logic as if that expression was a method call with a declared type of Comparable[]. It does not look inside to figure out the actual type.

So the compiler will accept your typecast, because it might succeed. It will only reject casts that cannot work out at all (according to declared types), such as casting Integer to String.

Note that it is probably a design flaw to allow this co-variance in arrays. You can cast Integer[] to Comparable[], but this has problems and for this reasons you cannot cast List<Integer> to List<Comparable>.

Thilo
  • 257,207
  • 101
  • 511
  • 656
0
  1. Reason for ClassCastException is due to Heap Pollution. Find more details here http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ050
  2. Use case of new to an interface array / just interface is to fill it with any class object which implements that interface (or) give an anonymous inner class definition.
  • Heap pollution cannot happen with arrays, as they check the component type at runtime (it is not erased as it is with generic collections). You get ArrayStoreExceptions if you try to put in something bad. – Thilo Aug 10 '17 at 10:57
0

It is because you are performing a Narrowing Reference Conversion

The class Integer implements the Comparable interface:

public final class Integer extends Number implements Comparable<Integer>

see: 5.1.6. Narrowing Reference Conversion

From any array type SC[] to any array type TC[], provided that SC and TC are reference types and there is a narrowing reference conversion from SC to TC.

Such conversions require a test at run time to find out whether the actual reference value is a legitimate value of the new type. If not, then a ClassCastException is thrown.

Community
  • 1
  • 1