Java:使用显式子类数组调用的 Vararg 方法

Java: Vararg method called with explicit subclass array

考虑以下示例,忽略人们想要这样做的原因:

private static class Original {
    public String getValue() {
        return "Foo";
    }
}

private static class Wrapper extends Original {
    private Original orig;

    public Wrapper(Original orig) {
        this.orig = orig;
    }

    @Override
    public String getValue() {
        return orig.getValue();
    }
}

public static void test(Original... o) {
    if (o != null && o.length > 0) {
        for (int i = 0; i < o.length; i++) {
            if (o[i] instanceof Wrapper) {
                o[i] = ((Wrapper) o[i]).orig; // Throws java.lang.ArrayStoreException at runtime
            }
        }
    }
}

public static void main(String[] args){
    test(new Wrapper[] { // Explicitly create an array of subclass type
        new Wrapper(new Original())
    });
}

这个例子在编译时没有给出警告或错误。似乎编译器决定 Wrapper[] 包含 Wrapper 个实例,这实际上意味着这些绝对是 Original class 的实例。这很好。

但是,在运行时,Wrapper[] 实例直接 传递到方法中。我认为在运行时拆除这个数组并重新创建 Original[] 的实例会很聪明,但似乎并非如此。

是否曾在某处(如 JLS)记录过此行为?像我这样的普通程序员总是假设我可以操纵 Original... 的可变参数,就好像它是 Original[].

是的,当WrapperOriginal时,Wrapper[]也是Original[](当我意识到这一点时,我也很惊讶)。

您的 WrapperOriginal 的子类型,因为它扩展了 Original class.

是的,如果调用的方法试图将不是 WrapperOriginal 存储到传递的大批。但这在编译时没有检查。据我了解,这正是我们拥有 ArrayStoreException 类型的原因,因为通常其他将错误类型存储到数组中的尝试会在编译时被捕获。 the documentation of ArrayStoreException 中有一个很好的简短示例。该示例还表明它与可变参数或方法调用没有任何关系,它适用于所有数组。

Java 语言从版本 1 开始就是以这种方式设计的(顺便说一句,在引入可变参数之前很久)。感谢 Andy Turner 找到 Java 语言规范 (JLS) 参考:它位于 section 4.10.3 Subtyping among Array Types:

If S and T are both reference types, then S[] >_1 T[] iff S >_1 T.