【问题标题】:int[] arr2 = arr1.clone() compiles without casting RHS (Object) to (int[]). Why derivedObj = baseObj allowed with no cast to Derived (i.e. int[])?int[] arr2 = arr1.clone() 编译时不将 RHS (Object) 转换为 (int[])。为什么在没有强制转换为 Derived(即 int[])的情况下允许 derivedObj = baseObj?
【发布时间】:2017-08-02 10:44:25
【问题描述】:

int[] 是派生自java.lang.Object 的类型(它的子类型)。 所以应该禁止分配int[] array = new Object()(没有强制转换),因为我不能写(没有强制转换)derivedObj = baseObj(但我可以写derivedObj = (Derived) baseObj)。

为什么下面的代码可以正常编译(并在运行时工作)?如果没有像int[] ar2 = (int[]) ar1.clone(); 那样显式转换,则克隆返回的Object 类型不能隐式转换为Object 的子类型(即int[])会报错,这也可以编译并正常工作。

    int[] ar1 = { 1, 2, 3, 4, 5 };
    //now we assign Base class to Derived class without explicit cast
    int[] ar2 = ar1.clone(); // why no compile ERR here???
    System.out.println(Arrays.toString(ar2)); // output: [1, 2, 3, 4, 5]

但下面的代码将无法编译,我理解为什么:如果没有显式转换(将 int[] 视为 Object 的子类),我们无法从 Base 类转换为 Derived 类。

Object obj = new Object();
ar2 = obj; // c.ERR!! cannot convert from Object to int[]

我的猜测是这样的:clone() 在原始数组中被覆盖以返回不是 Object,而是 Object 的子类,因此 int[].clone() 返回 int[] !!! 它被覆盖的方法可以返回子类型(更专业的类型)。此外,int[] "class" 中的这个被覆盖的 clone() 也将其可见性从 protected 更改为 public(在覆盖时也允许增加可见性)。

概念证明:

int[] arr = { 1, 2, 3 };        
System.out.println((arr.clone()).getClass()); // class [I
System.out.println((arr.clone()).getClass().getCanonicalName()); //int[]

更多实验:

与 Object 中的 clone() 具有相同签名的用户定义方法(参见下面的 sn-p)会产生编译错误,这与上面最顶层 sn-p 中的 clone() 不同。另请注意,在下面的 sn-p 中,方法返回 int[] 为“return arr”还是“return (Object) arr”):

public class MyTest {

    static Object returns_arr_as_Object() {
        int[] arr = { 1, 2, 3 };
        return arr; // snippet don't change if add cast: (Object) arr
    }

    public static void main(String[] args) {

        Object obj = returns_arr_as_Object(); // fine!

        int[] myarr;        
        myarr = returns_arr_as_Object(); // c.ERR! can't Object -> int[]

但是如果我们将 return_arr_as_Object() 从 Object 更改为 int[],则 sn-p 可以正常编译!

附加信息:

Java 语言规范第 4.3.1 节:“数组是对象”+“The 数组类型的超类型关系与超类关系不同。 Integer[] 的直接超类型是 Number[] 根据 §4.10.3,但 Integer[] 的直接超类是 Object 根据 Integer[] 的 Class 对象(第 10.8 节)。这无关紧要 练习,因为Object也是所有数组类型的superTYPE。”

数组是协变的(与参数化的泛型不同,它是不变的),这意味着 Integer[] 是 Object[](但反之亦然,并且似乎不适用于 int[] vs Object[])

其他实验:

片段 1:

int[] ar1 = { 1, 2 };
int[] ar2 = { 10, 20 };
Object obj = ar2; // now compiler knows that obj points to int[]
ar1 = obj; // c.ERR: cannot convert from Object to int[]

片段 2:

    int[] ar1 = { 1, 2 };
    int[] ar2 = { 10, 20 };
    Integer[] arInteger = { 10, 20 };

    Object[] objArr = ar2; // c.ERR: can't from int[] to Object[] 
    Object obj = ar2; // COMPILES!, but useless: obj[0] is c.ERR
    ar1 = obj; // c.ERR: cannot convert from Object to int[]
    arInteger = obj; // c.ERR: cannot convert from Object to Integer[]

    Object obj2 = arInteger;
    ar1 = obj2; // c.ERR: cannot convert from Object to int[]
    arInteger = obj2; // c.ERR: cannot convert from Object to Integer[]

    Object[] obj2Arr = arInteger; // COMPILES FINE !!!
    ar1 = obj2Arr; // c.ERR: can't convert from Object[] to int[]
    arInteger = obj2Arr; // c.ERR: can't from Object[] to Integer[]

    Object[] oArr = ar2; // c.ERR: cannot convert from int[] to Object[]
    oArr = arInteger; // COMPILES!
    System.out.println(oArr[0]); // output: 10

更多链接:cast primitive array to Object and back

附:我的编译器是 Eclipse (NEON 2)。

【问题讨论】:

    标签: java arrays object


    【解决方案1】:

    Oracle API docs for Object.clone() method 明确指出以下内容,这完全解释了一切:

    注意所有数组都被认为实现了接口 Cloneable 和那个数组的克隆方法的返回类型 type T[] 是 T[] 其中 T 是任何引用或原始类型。

    【讨论】:

      【解决方案2】:

      编译器知道ar1.clone() 是专门的int[]。因此,它允许该分配没有问题。

      另一方面,编译器不知道obj 是否是int[]。因此,如果没有显式强制转换(即告诉编译器您知道自己在做什么,这是故意的),它将不允许该分配。

      【讨论】:

        【解决方案3】:

        这是因为 int[] 不是对象,它是一种数据结构,您可以将其视为一种数据类型。 Int[] 不是一个类,因此当您创建一个 int[] 时,您并没有创建一个对象。您正在创建一个具有 int[] 数据类型的变量。

        【讨论】:

        猜你喜欢
        • 2020-07-03
        • 1970-01-01
        • 1970-01-01
        • 2013-04-23
        • 1970-01-01
        • 1970-01-01
        • 2018-11-29
        • 1970-01-01
        相关资源
        最近更新 更多