为什么无法将Object[]强制转换为String[]?

11
  1. 无错误

Object[] a = new String[]{"12","34","56"};
String[] b = (String[]) a;
  • 没有错误

    Object a = new String[]{"12","34","56"};    
    String[] b = (String[]) a;
    
  • 运行时错误:ClassCastException

  • Object[] a = new Object[3];
    a[0] = "12";
    a[1] = "34";
    a[2] = "56";
    String[] b = (String[]) a;
    
  • 运行时错误:ClassCastException

  • Object[] a = {"12","34","56"};    
    String[] b = (String[]) a;
    
    当然,如果一个String[]是作为Object[]创建的,我们可以将它向下转换为Object[]
    我的问题是,当Object[]是作为Object[]创建的,但其中所有成员都是字符串时,为什么我们不能将其转换为String[]?这是因为安全原因还是只是没有实现这个功能呢?

    Assylias从JLS的角度提供了一个很好的答案。Alex解释了为什么JLS不允许这样做。所以我需要的是Alex的答案。谢谢大家。 - Don Li
    6个回答

    8

    我能想到的两个原因如下。

    第一,如果您更改了原始数组,则转换后的数组可能会变得无效。例如:

     Object[] a = {"12","34","56"};   
     String[] b = (String[]) a; // pretend this is legal. a and b now point to the same array
    
     a[0] = new Object(); // clearly ok
     String x = b[0]; // No longer a string! Bad things will happen!
    

    其次,你选择的示例非常简单,但如果你有一个非常大的Object[]数组,并且编译器不清楚是什么填充了它,那么它就无法验证数组的每个元素是否满足强制转换。
    Object[] a = new Object[10000];
    // lots of weird and whacky code to fill the array with strings
    
    String[] b= (String[]) a; // valid or no? The best-defined answer is to say no.
    

    5

    这在JLS #5.5.3中有定义。实质上,强制类型转换:

     r = new RC[]; TC[] t = (TC[]) r;
    

    只有当RC是TC的子类型(或者TC本身)时,“运行时”才适用,RC实际上只包含TCs是无关紧要的,r的编译时类型也不被使用(重要的是运行时类型):
    • 您可以这样写:r = new String[]; Object [] t =(Object [])r;,但是
    • 您不能这样写r = new Object []; String [] t =(String [])r;

    JLS摘录:

    如果T是一个数组类型TC [],即TC类型的组件数组,则除非以下情况之一成立,否则会引发运行时异常:

    • TC和RC是相同的原始类型。
    • TC和RC是引用类型并且可以通过这些运行时强制转换规则的递归应用将类型RC转换为TC。

    在例3和4中,RC = Object而TC = String,Object不是String的子类型。 在例1和2中,RC = String而TC = String,因此它有效。

    注意:此上下文中的类型是运行时类型。


    在JLS中,它是TC[] = (TC[]) RC[]。因此RC是Object而TC是String。如果它是String,那么Object可以转换为String。所以我仍然不理解。 - Don Li
    JLS没有解释为什么允许将object[]强制转换为String[]的情况1和2。 - Don Li
    我再详细解释一下。在你的第一个例子中,你没有将 Object[] 强制转换为 String[],而是将 String[](运行时类型)强制转换为 String[],这是允许的。 - assylias

    4
    因为您没有转换数组的单个成员,而是转换了整个数组实例,该实例的类型为Object[]而不是String[]
    Object[] a = new String[]{"12","34","56"};
    

    这里实例的类型是String[],而编译时类型是Object[]。接下来一行你将其强制转换回String[],因为实际类型或运行时类型是String[],所以这样做是允许的。
    但是Object[] a = new Object[3]; 这里的实际类型和编译时类型都是Object[],而不是String[]。 因此,Object[]不能被视为String[]
    Object[] a = new Object[1];
    a[0] = "a"; //Actual type String 
    

    那么你可以这样做:

    String aStr = (String)a[0];
    

    2

    数组对象不仅仅是元素的集合,它们像其他对象一样拥有类。字符串数组的类是对象数组的子类。这就是为什么你的1或2没有错误,但最后两个等价于

    Object o = new Object();
    String s = (String) o;
    

    这很有趣。你能告诉我class array的名称和String[]类的类名吗? - Don Li
    1
    @DonLi:它们没有友好的名称(除了Object[]String[])。内部名称类似于[Ljava.lang.Object;[Ljava.lang.String; - cHao
    1
    字符串数组的类名是[Ljava.lang.String;。由于它不是有效的标识符,您不能直接在程序中使用它,但是您可以在反射API中使用它。 - Joni
    1
    你可以定义一个数组,并在该数组实例上执行 getClass(),然后打印出来,你将得到该表示。 - Narendra Pathai
    1
    更简单的是:类对象字面量String[].class - Joni

    2
    如果在强制转换时,所有成员对象数组都是字符串,那么您仍然可以将不是字符串的对象分配给稍后该数组的元素。因此,您将拥有元素不是字符串的字符串数组。

    0

    这个文章提供了一种快速创建String[]的方法,可以从Object[]中获取。

    arrayOfUrls = imageUrls.toArray(new String[imageUrls.size()]);
    

    假设当然是 imageUrls 不是 null

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接