【问题标题】:Java: Vararg method called with explicit subclass array [duplicate]Java:使用显式子类数组调用的 Vararg 方法
【发布时间】:2018-12-11 08:36:02
【问题描述】:

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

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... 的 vararg 参数,就好像它是 Original[] 一样。

【问题讨论】:

  • 那么确切的问题是什么?
  • 这个问题是Java泛型不变的原因(例如List&lt;Dog&gt;不是List&lt;Animal&gt;)。
  • JLS Sec 4.10.3: "如果 S 和 T 都是引用类型,则 S[] >1 T[] iff S >1 T。"
  • 我有点困惑o 的类型是Wrapper[] 而不是Original[] 传递的参数不应该放在可变参数数组中吗?或者与参数类型匹配的数组是否有特殊例外?
  • 不,@markspace,因为Wrapper 的数组不是Original,你不能将它放在Original 的数组中。从其他引用类型你可能知道对象的运行时类型可以比引用变量的声明类型更精确。这对于数组也是可能的:o 的声明类型是Original 的数组,但运行时类型是Wrapper 的数组。

标签: java variadic-functions jls


【解决方案1】:

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

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

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

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

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

【讨论】:

  • 是的,我太着迷于它发生在可变参数上下文中的事实,以至于我没有想到这只是数组的问题。尽管这个问题已经被标记为重复,但我会将这个答案标记为正确,因为我认为找到确切的 JLS 子句来支持这一点是件好事。
猜你喜欢
  • 2012-01-08
  • 1970-01-01
  • 2015-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-01
  • 1970-01-01
相关资源
最近更新 更多