如何在Java中创建通用数组?

如何在Java中创建通用数组?,java,arrays,generics,reflection,instantiation,Java,Arrays,Generics,Reflection,Instantiation,由于Java泛型的实现,您不能有这样的代码: public class GenSet<E> { private E a[]; public GenSet() { a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation } } 公共级发电机组{ 私人E a【】; 公共发电机组(){ a=新的E[初始数组长度];//错误:一般数组创建 } } 如何在维护类型安全的同时

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

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

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
    }
}
公共级发电机组{
私人E a【】;
公共发电机组(){
a=新的E[初始数组长度];//错误:一般数组创建
}
}
如何在维护类型安全的同时实现此功能

我在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;
}
import java.lang.reflect.Array;
类堆栈{
公共堆栈(类clazz,int容量){
array=(T[])array.newInstance(clazz,capacity);
}
私有final T[]数组;
}

但是我真的不明白发生了什么。

这个例子是使用Java反射创建一个数组。通常不建议这样做,因为它不是类型安全的。相反,您应该做的只是使用一个内部列表,并且完全避免使用数组。

您可以创建一个对象数组,并将其强制转换为E everywhere。是的,这不是一种非常干净的方法,但至少应该有效。

第25项的第5章(泛型)介绍了这一点。首选列表而非数组。

您的代码将正常工作,尽管它将生成未经检查的警告(您可以使用以下注释抑制该警告):

@SuppressWarnings({"unchecked"})
但是,最好使用列表而不是数组


有一个关于这个错误/特性的有趣的讨论。

我必须问一个问题作为回报:您的
发电机组是“选中”还是“未选中”?
这是什么意思

  • 选中:strong键入。
    GenSet
    明确知道它包含什么类型的对象(即,它的构造函数是用
    参数显式调用的,当传递的参数不是
    e
    类型时,方法将引发异常。请参阅

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

    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];
        }
    }
    
    公共级发电机组{
    私人E[]a;
    公用发电机组(c类,int s){
    //使用数组本机方法创建数组
    //仅在运行时已知的类型
    @抑制警告(“未选中”)
    最终的E[]a=(E[])数组;
    这个a=a;
    }
    E get(int i){
    返回一个[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;
        }
    }
    
    公共级发电机组{
    私有对象[]a;
    公共发电机组(int s){
    a=新对象;
    }
    E get(int i){
    @抑制警告(“未选中”)
    最终E=(E)a[i];
    返回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];
        }
    
        ...
    }
    
    公共类发电机组{//E有一个Foo的上界
    私有的Foo[]a;//E擦除为Foo,所以使用Foo[]
    公共发电机组(int s){
    a=新的Foo[s];
    }
    ...
    }
    

所有这些都源于Java中泛型的一个已知的、故意的弱点:它是使用擦除实现的,因此“泛型”类不知道它们在运行时使用的是什么类型参数,因此除非有明确的机制(类型检查),否则无法提供类型安全性Java泛型通过在编译时检查类型并插入适当的强制转换来工作,但会删除编译文件中的类型。这使得不理解泛型的代码可以使用泛型库(这是一个深思熟虑的设计决策)但这意味着您通常无法在运行时找到类型

公共
堆栈(类clazz,int容量)
构造函数要求您在运行时传递类对象,这意味着类信息在运行时可用于需要它的代码。而
表单意味着编译器将检查您传递的类对象是否正是类型T的类对象。不是T的子类,也不是T的超类,而恰恰是T。

这意味着您可以在构造函数中创建适当类型的数组对象,这意味着您存储在集合中的对象类型将在添加到集合时检查其类型。

您可以执行以下操作:

E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];
这是在有效的Java中实现泛型集合的建议方法之一;第26项。无类型错误,无需重复强制转换数组。但是,这会触发警告,因为它具有潜在危险,应谨慎使用。如注释中所述,此
对象[]
现在伪装成我们的
E[]
类型,如果使用不安全,可能会导致意外错误或
ClassCastException
s

根据经验,只要强制转换数组在内部使用(例如,用于支持数据结构),并且不返回或暴露给客户端代码,这种行为是安全的。如果需要将泛型类型的数组返回给其他代码,您提到的反射
数组
类是正确的方法



值得一提的是,如果您使用泛型,在任何可能的情况下,使用
List
s都会比使用数组快乐得多。当然,有时您没有选择余地,但使用集合框架要强大得多。

下面介绍如何使用泛型来获得与您要查找的类型完全相同的数组ile保留类型安全性(与其他答案相反,其他答案要么返回
对象
数组,要么在编译时产生警告):

Class
中返回
Class
对象的每个方法也是如此

关于Joachim Sauer的评论(我自己没有足够的声誉对此发表评论),使用cast to
t[]
的示例将导致警告,因为在这种情况下编译器无法保证类型安全


编辑关于国际非政府组织的评论:

public static <T> T[] newArray(Class<T[]> type, int size) {
   return type.cast(Array.newInstance(type.getComponentType(), size));
}
publicstatict[]newArray(类类型,int-size){
返回type.cast(Array.newInstance(type.g
String foo = String[].class.getComponentType().cast("bar"); // won't compile
public static <T> T[] newArray(Class<T[]> type, int size) {
   return type.cast(Array.newInstance(type.getComponentType(), size));
}
private int m = 0;
private int n = 0;
private Element<T>[][] elements = null;

public MatrixData(int m, int n)
{
    this.m = m;
    this.n = n;

    this.elements = new Element[m][n];
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            this.elements[i][j] = new Element<T>();
        }
    }
}
E[] a;

a = newArray(size);

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}
public class Whatever<Thing>{
    private class Holder<OtherThing>{
        OtherThing thing;
    }
    public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}
Object attributeValue = null;
try {
    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }
    else if(!clazz.isInterface()){
        attributeValue = BeanUtils.instantiateClass(clazz);
    }
} catch (Exception e) {
    logger.debug("Cannot instanciate \"{}\"", new Object[]{clazz});
}
    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }
public static <T> T[] toArray(final List<T> obj) {
    if (obj == null || obj.isEmpty()) {
        return null;
    }
    final T t = obj.get(0);
    final T[] res = (T[]) Array.newInstance(t.getClass(), obj.size());
    for (int i = 0; i < obj.size(); i++) {
        res[i] = obj.get(i);
    }
    return res;
}
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);
Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];
 Tuple<Long, String>[] tupleArray = new Tuple[10];
Item<K>[] array = new Item[SIZE];
private K value;
@FunctionalInterface
interface ArraySupplier<E> {
    E[] get(int length);
}

class GenericSet<E> {
    private final ArraySupplier<E> supplier;
    private E[] array;

    GenericSet(ArraySupplier<E> supplier) {
        this.supplier = supplier;
        this.array    = supplier.get(10);
    }

    public static void main(String[] args) {
        GenericSet<String> ofString =
            new GenericSet<>(String[]::new);
        GenericSet<Double> ofDouble =
            new GenericSet<>(Double[]::new);
    }
}
public T [] createArray(int desiredSize){
    ArrayList<T> builder = new ArrayList<T>();
    for(int x=0;x<desiredSize;x++){
        builder.add(null);
    }
    return builder.toArray(zeroArray());
}

//zeroArray should, in theory, create a zero-sized array of T
//when it is not given any parameters.

private T [] zeroArray(T... i){
    return i;
}
public class GenSet<Item> {
    private Item[] a;

    public GenSet(int s) {
        a = (Item[]) new Object[s];
    }
}
private E a[];
private int size;

public GenSet(int elem)
{
    size = elem;
    a = (E[]) new E[size];
}
T[] array = (T[])new Object[SIZE];
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;
}
Stack<foo> = new Stack<foo>(foo.class,50)
Array.newInstance(clazz, capacity);
@SafeVarargs
public static <T> T[] toGenericArray(T ... elems) {
    return elems;
}
List<Person>[] personLists=new ArrayList<Person>()[10];
import java.util.ArrayList;
import java.util.List;


public class PersonList {

    List<Person> people;

    public PersonList()
    {
        people=new ArrayList<Person>();
    }
}
PersonList[] personLists=new PersonList[10];
class GenArray <T> {
    private T theArray[]; // reference array

    // ...

    GenArray(T[] arr) {
        theArray = arr;
    }

    // Do whatever with the array...
}
class GenArrayDemo {
    public static void main(String[] args) {
        int size = 10; // array size
        // Here we can instantiate the array of the type we want, say Character (no primitive types allowed in generics)
        Character[] ar = new Character[size];

        GenArray<Character> = new Character<>(ar); // create the generic Array

        // ...

    }
}
class GenericInvoker <T> {
    T variable;
    public GenericInvoker(T variable){
        this.variable = variable;
    }
}
GenericInvoker<T>[] array;
public MyArray(){
    array = new GenericInvoker[];
}
public T get(int index){
    return array[index].variable;
}
public void resize(int newSize){
    array = Arrays.copyOf(array, newSize);
}
public boolean add(T element){
    // the variable size below is equal to how many times the add function has been called 
    // and is used to keep track of where to put the next variable in the array
    arrays[size] = new GenericInvoker(element);
    size++;
}
public class GenSet<T> {

    private final T[] array;

    @SafeVarargs
    public GenSet(int capacity, T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
              "Do not provide values for dummy argument.");
        this.array = Arrays.copyOf(dummy, capacity);
    }

    @Override
    public String toString() {
        return "GenSet of " + array.getClass().getComponentType().getName()
            + "[" + array.length + "]";
    }
}
GenSet<Integer> intSet = new GenSet<>(3);
System.out.println(intSet);
System.out.println(new GenSet<String>(2));
GenSet of java.lang.Integer[3]
GenSet of java.lang.String[2]
public <T> T[] array(T... values) {
    return values;
}
class Stack<T> {
private final T[] array;
public Stack(int capacity) {
    array = (T[]) new Object[capacity];
 }
}
GenSet<Integer> intSet[] = new GenSet[3];
for (int i = 0; i < 3; i++)
{
   intSet[i] = new GenSet<Integer>();
}