1

I have a class Shape and a class Circle that extends Shape as follows:

public class Shape {
  private double area;
  public Shape(double thatArea) {
      this.area = thatArea;
  }

  public double getArea() {
      return this.area;
  }
}

public class Circle extends Shape {
    private double radius;
    public Circle(double radius) {
        super(3.14*radius*radius);
    }

    public double getRadius() {
        return this.radius;
    }
}

Supposed that I create an array of shapes as follows

Shape[] shapes = new Shape[3];
shapes[0] = new Shape(100.0);
shapes[1] = new Circle(5.0);
shapes[2] = new Shape(35.0);

And have a filter by area method like this

public Shape[] filterByMaxArea(double maxArea) {
    ArrayList<Shape> shapeResult = new ArrayList<>();

    for (Shape s : shapes) {
        if (s.getArea < maxArea) shapeResult.add(s);
    }

    return (Shape[]) shapeResult.toArray();
}

If I call the filterByMaxArea() method like this

filterByMaxArea(1000); // in order to include the 
circle too

Then the compiler throws a ClassCastException

Exception in thread "main" java.lang.ClassCastException: java.base/[Ljava.lang.Object; cannot be cast to [Lmypackage.Shape;
at mypackage.Main.filterByMaxArea(Main.java:74)

Where line 74 in the code is this

return (Shape[]) shapeResult.toArray();

Can someone explain me why does the cast fail even though the Circle is a subtype of Shape and previously I had an array of Shape and Circle instances ?

P.S I have tried also declaring the ArrayList with bounded wildcards without any luck.

 ArrayList<? extends Shape> shapeResult = new ArrayList<>();
Lucian Enache
  • 2,210
  • 5
  • 31
  • 55

3 Answers3

3

toArray() returns Object[], there is a overloaded version of toArray(T[] a) which will give the desired result, use it like

return shapeResult.toArray(new Shape[shapeResult.size()]);
Ramanlfc
  • 7,900
  • 1
  • 14
  • 24
  • I was thinking about using it, is that the only way to cast it back ? Would you mind explaining how and why would it work ? – Lucian Enache Jan 17 '18 at 21:00
  • 1
    @LucianEnache java generic erasure causes issues in creating generic arrays, we have to ultimately use `Array.newInstance()`. i haven't looked at `toArray(T[] a)` implementation but i believe that's what it uses, here's more: https://stackoverflow.com/questions/529085/how-to-create-a-generic-array-in-java – Ramanlfc Jan 17 '18 at 21:04
  • 1
    @Lucian Enache Generics appeared in Java 1.5 but before Java 1.5, the `Object[] toArray()` method was already defined in the Collection interface. So Java API developers kept them in Java 1.5 and later version to not break the API. In most of cases (as you use generic type) this overloaded method is not convenient. Use `T[] toArray(T[] a)` rather – davidxxx Jan 17 '18 at 21:21
1

toArray() is overloaded to return an array of the type of the generic declared in the collection :

T[] List.toArray(T[] a)

But in fact you don't need that.
You could use Stream.toArray(IntFunction) to create a array of Shape from the List.

With the actual code you could do :

return shapeResult.stream().toArray(Shape[]::new);

that is more elegant than :

return shapeResult.toArray(new Shape[shapeResult.size()]);

Or by using a full stream approach, the whole method could be written :

public Shape[] filterByMaxArea(double maxArea) {
     return Arrays.stream(shapes)
                  .filter(s -> s.getArea() < maxArea)
                  .toArray(Shape[]::new);
}
davidxxx
  • 104,693
  • 13
  • 159
  • 179
0

To the point of your question:

Lets look at 2 examples with arrays:

This works:

Object[] array = new MyObject[2];
MyObject[] myarrau = (MyObject[])os; //ok, since array is of type MyObject[] 

This does not (gives java.lang.ClassCastException like in your case)

Object[] array = new Object[2];
MyObject[] myarrau = (MyObject[])os; //ClassCastException  here since array is not type of MyObject[]

And snippet from toArray code of the ArrayList class:

public Object[] toArray() {
    Object[] r = new Object[size()];
    return r;
}

As you see the returned array is of Object[] type like in above second example.

tsolakp
  • 5,560
  • 1
  • 17
  • 22