class 和超级 class 数组的有界通配符

Bounded wildcards for class and super class array

我有一个 class Shape 和一个 class Circle 扩展 Shape 如下:

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;
    }
}

假设我创建一个形状数组如下

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

并且有一个像这样的按区域过滤的方法

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();
}

如果我这样调用 filterByMaxArea() 方法

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

然后编译器抛出一个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)

代码中第74行是这样的

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

有人可以解释为什么即使 Circle 是 Shape 的子类型并且之前我有一个 Shape 和 Circle 实例数组,转换仍然失败吗?

P.S 我也试过用有界通配符声明 ArrayList,但没有成功。

 ArrayList<? extends Shape> shapeResult = new ArrayList<>();

toArray() returns Object[],有一个重载版本的 toArray(T[] a) 会给出想要的结果,像

一样使用它
return shapeResult.toArray(new Shape[shapeResult.size()]);

toArray() 被重载为 return 集合中声明的泛型类型的数组:

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

但实际上你不需要那个。
您可以使用 Stream.toArray(IntFunction)List.

创建一个 Shape 的数组

使用实际代码你可以做到:

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

比 :

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

或者通过使用全流方法,整个方法可以写成:

public Shape[] filterByMaxArea(double maxArea) {
     return Arrays.stream(shapes)
                  .filter(s -> s.getArea() < maxArea)
                  .toArray(Shape[]::new);
}

针对您的问题:

让我们看两个数组示例:

有效:

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

这不会(与您的情况一样 java.lang.ClassCastException)

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

以及 ArrayList class 的 toArray 代码片段:

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

如您所见,返回的数组是 Object[] 类型,如上面的第二个示例。