时间:2019-05-30 05:24:15

标签: java generics

更新:尽管我已经找到了答案,但看来我对问题的描述不是很清楚。所以我做了很多修改,以使其他人理解我的问题。


我对为什么不允许使用参数化类型的数组的理解:

Integer[] b = {1,2,3};
Object[] a = b;
String[] c = {"123"};
a[0] = c; // Exception in thread "main" java.lang.ArrayStoreException
Integer i = b[0];
// Not really allowed.
List<String>[] lsa = new List<String>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// Unsound, but passes run time store check
oa[1] = li;

// Run-time error: ClassCastException.
String s = lsa[1].get(0);

这两段代码具有相同的结构。

在第一篇中,Object[] a = b使得ab都指向同一个变量b。因此a[0] = c实际上正在更改b。这种操作在编译时不会引起任何警告,但会引发ArrayStoreException。

在第二篇中,Object o = lsa; Object[] oa = (Object[]) o;使得oalsa都指向同一个变量lsaoa[1] = li;  实际上正在更改lsa

所以我认为这就是为什么不允许使用参数化类型的数组的原因。

如果我的理解正确,那么T[] myArray之类的情况也应拒绝。因为下面的代码也具有相同的结构。

import java.util.*;

public class Test {
    public static <T> void test(T[] t) {
        Object[] a = t;
        String[] c = {"123"};
        a[0] = c;
        T i = t[0];
    }

    public static void main(String[] args) {
        Integer[] t = {1,2,3};
        test(t);
    }
}

但是在Java教程中,

<T> T[] makeArray(T t) {
    return new T[100]; // Error.
}

给出的原因是

  

由于类型变量在运行时不存在,因此无法确定实际的数组类型。

不是我期望的原因。

它也说

  

解决这些限制的方法是将类文字用作运行时类型标记

所以我在Generics gotchas找到了一个例子。 “未走的路”部分中的代码:

public class ArrayList<V> implements List<V> {
  private V[] backingArray;
  private Class<V> elementType;

  public ArrayList(Class<V> elementType) {
    this.elementType = elementType;
    backingArray = (V[]) Array.newInstance(elementType, DEFAULT_LENGTH);
  }
}

似乎使用类文字已经解决了使用类型为变量(T [])的数组的问题。但是我一开始提到的结构问题仍然存在。回想一下,我认为这三段代码的结构都不会导致编译警告,但会导致运行时错误。

那么,我的理解出了什么问题?

非常感谢您的时间:)

2 个答案:

答案 0 :(得分:0)


第一个问题

  

为什么不允许使用参数化类型的数组?

我发现the answer

  

我可以创建一个其组件类型为具体参数化类型的数组吗?

     

否,因为它不是类型安全的。


然后,我试图解释示例代码为何引发异常?

  1. java编译在编译期间未发现问题,每种类型的转换看起来都是合法的。 (ArrayStoreException是RuntimeException)

  2. 当您在String中放置Integer Array元素时,会发生异常。 您可以使用javacjavap查看字节码。 T[] t的类型为Object [],因此变量a(引用类型)将引用Integer[]对象。当您将String元素放入Integer Array

  3. 时发生了异常

我认为问题与java泛型无关,所以我将示例更改如下以重现异常。

public class Test {

    public static void main(String[] args) {
        Integer[] t = {1,2,3};
        Object[] a = t;
        a[2] = "aaa";
    }
}

答案 1 :(得分:0)

您已经发现,不能将元素分配给可更改类型的数组:

String[] ss = {""};
Object[] oo = ss;
oo[0] = 0; // ArrayStoreException

这是因为在运行时检查了数组元素的类型,可能是因为所有类型信息仍然可用。

但是对于通用数组,例如List<String>[],“ String”位在运行时不可用。因此,这将(名义上)有效:

List<String>[] lss = {Arrays.asList()};
Object[] oo = lss;
List<Integer> is = Arrays.asList(0);
oo[0] = is;

这会很好,因为所有可以检查的是您正在存储List的实例。

但是,如果这样做,那么它将失败:

String s = lss[0].get(0); // ClassCastException

此故障可能发生在与导致该故障的任务相距很远的地方。

因此,不允许创建通用数组。


关于List<String>[]T[]之间的区别:确实没有区别。 List<String>T拥有的有效类型。

关键是您无法创建前者的实例:该类型的引用可以安全地获取的唯一值是null

  

似乎使用类文字已经解决了“类型变量在运行时不存在”的问题

否,因为类文字仅适用于非参数化类型:您可以通过传递ArrayList<String>作为元素类型来创建String.class。您无法创建ArrayList<ArrayList<String>>,因为没有ArrayList<String>.class