java - java如何创建泛型数组?

  显示原文与译文双语对照的内容

由于Java泛型的实现,你不能有这样的代码:


public class GenSet<E> {
 private E a[];

 public GenSet() {
 a = new E[INITIAL_ARRAY_LENGTH];//error: generic array creation
 }
}

如何在维护类型安全时实现这里功能?

我在Java论坛上看到一个类似这样的解决方案:


import java.lang.reflect.Array;

class Stack<T> {
 public Stack(Class<T> clazz, int capacity) {
 array = (T[])Array.newInstance(clazz, capacity);
 }

 private final T[] array;
}

但是我真的不明白。 任何人都能帮助?

时间:

我必须回答一个问题: 你的GenSet"已经检查"或者"未选中"? 这意味着什么呢?

  • 检查: 强打字 。 GenSet 明确知道它包含的对象类型( 例如 。 它的构造函数是用 Class<E> 参数显式调用的,并且方法在传递不是 E 类型的参数时抛出异常。 参见 Collections.checkedCollection web 。

    -> 在这种情况下,你应该编写:

    
    public class GenSet<E> {
    
     private E[] a;
    
     public GenSet(Class<E> c, int s) {
    //Use Array native method to create array
    //of a type only known at run time
     @SuppressWarnings("unchecked")
     final E[] a = (E[]) Array.newInstance(c, s);
     this.a = a;
     }
    
     E get(int i) {
     return a[i];
     }
    }
    
    
  • 未选中: 弱类型 。 在作为参数传递的任何对象上实际上没有进行类型检查。

    - 在这种情况下,你应该写

    
    public class GenSet<E> {
    
     private Object[] a;
    
     public GenSet(int s) {
     a = new Object[s];
     }
    
     E get(int i) {
     @SuppressWarnings("unchecked")
     final E e = (E) a[i];
     return e;
     }
    }
    
    

    注意,数组的组件类型应该是类型参数的擦除

    
    public class GenSet<E extends Foo> {//E has an upper bound of Foo
    
     private Foo[] a;//E erases to Foo, so use Foo[]
    
     public GenSet(int s) {
     a = new Foo[s];
     }
    
    . . .
    }
    
    

所有这些结果都源于Java中的一个已知和蓄意的缺点: 它是使用erase实现的,所以"通用"类不知道在运行时创建的类型参数,因此不能提供 type-safety,除非实现了显式机制( type-checking ) 。

快速测试确认你也可以这样做:


E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

我的测试:


public class ArrTest<E> {
 public static void main(String[] args){
 ArrTest<String> t = new ArrTest<String>();
 t.test("Hello World");
 }

 public void test(E a){
 E[] b = (E[])new Object[1];
 b[0] = a;
 System.out.println(b[0]);
 }
}

无警告,没有类型错误,不需要重复转换数组。 这是潜在的危险,而应慎用。 就像评论中详细说明的那样,这个 Object[] 现在伪装为我们的E[] 类型,并且会导致意外的错误或者 ClassCastException ( 如果使用不安全) 。

根据经验,只要转换数组在内部使用,而不返回或者暴露到客户端代码,这里行为就是安全的。 如果需要将泛型类型的数组返回到其他代码,那么你提到的反射 Array 类是正确的方法。


值得注意的是,如果你使用泛型,你将会在使用 List 而不是数组的时候更快乐。 当然,有时候你没有选择,但是使用集合框架更加健壮。

要扩展到更多维度,只需将 [] 和维度参数添加到 newInstance() ( T 是一个类型参数,cls 是一个 Class<T>d1 通过 d5 是整数):


T[] array = (T[])Array.newInstance(cls, d1);
T[][] array = (T[][])Array.newInstance(cls, d1, d2);
T[][][] array = (T[][][])Array.newInstance(cls, d1, d2, d3);
T[][][][] array = (T[][][][])Array.newInstance(cls, d1, d2, d3, d4);
T[][][][][] array = (T[][][][][])Array.newInstance(cls, d1, d2, d3, d4, d5);

有关详细信息,请参阅 Array.newInstance()

这是唯一一个安全类型的答案


E[] a;

a = newArray(size);

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
 return Arrays.copyOf(array, length);
}

尽管线程已经死了,但我还是想引起你的注意:

泛型在编译时用于类型检查:

  • 因此,目的是检查你需要的是什么。
  • 你所返回的是消费者所需要的。
  • 检查:

enter image description here

在编写泛型类时不要担心转换警告。 使用时担心。

这是在第 5章的( 泛型) 中介绍的Effective Java,2和版本,项目 25.。 更喜欢列表与数组

你的代码将工作,但它将生成一个未经检查的警告( 你可以通过以下注释抑制它:


@SuppressWarnings({"unchecked"})

但是,最好使用列表而不是数组。

太阳, 站点上,有一个有趣的讨论这部 Bug/feature.

Java泛型通过在编译时检查类型并插入适当的类型转换来工作,但是清除已经编译文件中的类型。 这使得泛型库可以通过不理解泛型( 这是一个深思熟虑的设计决策)的代码来使用,但这意味着你通常无法找到运行时的类型。

公众 Stack(Class<T> clazz,int capacity) 构造函数要求你在运行时传递类对象,这意味着类信息在运行时不可用,以便代码需要它。 而 Class<T> 窗体表示编译器将检查你传递的类对象是否是类型T 对象for的类对象。 不是T的子类,不是T的超类,而是精确的T 。

这意味着你可以在构造函数中创建适当类型的数组对象,这意味着你在集合中存储的对象的类型将在集合中选中它们的类型。

...