访问Object[]类型的数组时发生ClassCastException 请考虑下面的java代码(我从现实世界中获取了它,但简化了一点,删除了无关的细节):

访问Object[]类型的数组时发生ClassCastException 请考虑下面的java代码(我从现实世界中获取了它,但简化了一点,删除了无关的细节):,java,Java,另一方面,如果我将显式强制转换添加到对象[],它将毫无例外地工作: public class CastIssue<T> { @SuppressWarnings("unchecked") private T[] data = (T[]) new Object[1]; public static void main(final String[] args) { final CastIssue<Integer> issue = new CastIssue&

另一方面,如果我将显式强制转换添加到对象[],它将毫无例外地工作:

public class CastIssue<T> {
  @SuppressWarnings("unchecked")
  private T[] data = (T[]) new Object[1];

  public static void main(final String[] args) {
    final CastIssue<Integer> issue = new CastIssue<>();
    System.out.println(((Object[])issue.data).length);
  }
}
公共类问题{
@抑制警告(“未选中”)
私有T[]数据=(T[])新对象[1];
公共静态void main(最终字符串[]args){
最终CastIssue=新CastIssue();
System.out.println(((Object[])issue.data.length);
}
}
为什么在第一种情况下抛出异常

我使用JDK1.8.0(40


编辑:正如@wero所指出的,编译器在第一种情况下插入强制转换。我想了解为什么要在那里插入这个cast(很抱歉没有在原始问题中明确说明这一点)。对JLS或JVM规范的任何引用都是好的。

在编译类中,由于类型擦除,字段
private T[]data
的类型是
Object[]
。 但是当您访问它时(
issue.data.length,在您的第一个示例中是
),它被强制转换为整数数组。使用
javap
反编译类时,很容易看到这一点:

  public static void main(java.lang.String[]);
    Code:
       0: new           #1                  // class test/CastIssue
       3: dup
       4: invokespecial #24                 // Method "<init>":()V
       7: astore_1
       8: getstatic     #25                 // Field java/lang/System.out:Ljava/io/PrintStream;
      11: aload_1
      12: getfield      #14                 // Field data:[Ljava/lang/Object;
      15: checkcast     #31                 // class "[Ljava/lang/Integer;"
      18: arraylength
      19: invokevirtual #33                 // Method java/io/PrintStream.println:(I)V
      22: return

由于类型擦除,
T
实际上是
的非静态方法中的
对象
。在
main
方法中,
T
已知为
Integer
,因此
问题.data
引用隐式转换为
整数[]

整数[]
对象[]
的子类,同样地
整数
也是
对象
的子类

可以将
整数
分配给
对象
类型的变量,但不能将
对象
强制转换/分配给
整数
类型的变量

数组也是如此:可以将
整数[]
分配给
对象[]
类型的变量,但不能将
对象[]
强制转换/分配给
整数[]
类型的变量


那么,如何使用泛型呢

为了让代码构造
T
T[]
的实际实例,必须为其提供运行时假定的
T
的实际

您可以在构造函数上传入该类,并使用反射创建数组:

private T[] data;

@SuppressWarnings("unchecked")
public CastIssue(Class<T> clazz) {
    this.data = (T[])Array.newInstance(clazz, 1);
}
这样,主要方法就变成:

final CastIssue<Integer> issue = create(Integer.class);
System.out.println(issue.data.length);
final CastIssue=create(Integer.class);
System.out.println(issue.data.length);

这是否更好由您决定。

父类型可以容纳子类型。。否则您必须执行显式类型转换!是的,我看到了演员阵容。我很想知道,当我访问数组的length属性时,为什么编译器会将它插入其中。最好参考一下JLS或JVM规范。我可能应该编辑我的问题,以便更明确地说明它。@AndrieiPolunin我相信我在回答的第一段中已经谈到了这一点,尽管没有提到JLS。希望有帮助。
  public static void main(java.lang.String[]);
    Code:
       0: new           #1                  // class test/CastIssue
       3: dup
       4: invokespecial #24                 // Method "<init>":()V
       7: astore_1
       8: getstatic     #25                 // Field java/lang/System.out:Ljava/io/PrintStream;
      11: aload_1
      12: getfield      #14                 // Field data:[Ljava/lang/Object;
      15: arraylength
      16: invokevirtual #31                 // Method java/io/PrintStream.println:(I)V
      19: return    
private T[] data;

@SuppressWarnings("unchecked")
public CastIssue(Class<T> clazz) {
    this.data = (T[])Array.newInstance(clazz, 1);
}
final CastIssue<Integer> issue = new CastIssue<Integer>(Integer.class);
System.out.println(issue.data.length);
public static <T> CastIssue<T> create(Class<T> clazz) {
    return new CastIssue<T>(clazz);
}
final CastIssue<Integer> issue = create(Integer.class);
System.out.println(issue.data.length);