Array Subtyping.

我们来看一段 Java 代码,其在 Java 中是 type check 的。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Point { ... }    // has fields double x, y
class ColorPoint extends Point { ... } // adds field String color

void m1(Point[] pt_arr) {
pt_arr[0] = new Point(3, 4);
}

String m2(int x) {
ColorPoint[] cpt_arr = new ColorPoint[x];
for (int i = 0; i < x; ++i)
cpt_arr[i] = new ColorPoint(0, 0, "green");
m1(cpt_arr);
}

这样的操作称为 array subtyping:即 If ta <: tb, then ta[] <: tb[]。它本质上就是 depth subtyping。根据上文中我们总结的规律,Java 支持三个 features 中的两个:

  • Array subtyping.
  • Array indices are mutable.

那么 Java 的 type system 就无法阻止某些程序的 field-missing 错误。事实确实如此,上述程序能够通过 Java 的 type check。为了解决这个问题,Java 引入了 run-time checks

Java use run-time checks to maintain the invariant that an object of type t[] always holds objects that have type t or a subtype, but not a supertype.

于是当程序运行到 pt_arr[0] = new Point(3, 4) 这一语句时,Java 的 run-time check 会发现 ColorPoint[] 类的数组存储了其 supertype Point 类的对象,于是 ArrayStoreException 错误将被抛出。

Java 与 C# 以加入 run-time checks 的代价换取了 depth subtyping,这是因为 depth subtyping 自有其意义:它的确提升了语言的灵活性 (flexibility) 与 type system 的 expressiveness。

除了利用 run-time checks 之外,还有其他的方法可以达到相同的目的:

  • Bounded polymorphism. Use generics (parametric polymorphism) combined with subtyping.
  • Having support for indicating that a method will not update array elemtents, in which case depth subtyping is sound.