Java:使用显式子类数组

时间:2018-07-03 06:07:27

标签: java variadic-functions jls

考虑以下示例,忽略了这样做的原因:

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类的实例。很好。

但是,在运行时,Wrapper[]实例直接 传递到方法中。我曾经认为,删除该数组并在运行时重新创建Original[]的实例足够聪明,但是似乎并非如此。

是否曾经在某处(例如JLS)记录过此行为?像我这样的普通程序员总是会假设我可以像对待Original...一样操作Original[]的vararg参数。

1 个答案:

答案 0 :(得分:1)

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

您的WrapperOriginal的子类型,因为它消除了Original类。

是的,如果被调用的方法试图将不是ArrayStoreException的{​​{1}}存储到传递的数组中,则数组类型之间的子类型关系可能会引起Original。但这不是在编译时检查的。据我了解,这正是我们拥有Wrapper类型的原因,因为通常在编译时会捕获将错误类型存储到数组中的其他尝试。 the documentation of ArrayStoreException中有一个很好的简短示例。该示例还说明,它与所有数组的varargs或方法调用实际上无关。

Java语言是从版本1(很早就引入varargs,BTW)开始设计的。感谢Andy Turner查找Java语言规范(JLS)参考:它在section 4.10.3 Subtyping among Array Types中:

  

如果S和T都是引用类型,则S []> _1 T []如果S> _1 T。