【问题标题】:How to create a generic array in Java?如何在 Java 中创建一个泛型数组?
【发布时间】:2022-01-02 15:57:04
【问题描述】:

由于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;
}

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

【问题讨论】:

  • 你真的需要在这里使用数组吗?使用集合怎么样?
  • 是的,我也认为集合更适合解决这个问题。但这是课堂作业,它们是必需的:(
  • 我不明白为什么我需要在这里反映。Java 语法很奇怪:像 new java.util.HashMap[10] 无效。 new java.util.HashMap(10) 无效。 new long[][10] 无效,new long[10][] 有效。那东西让写一个能写java程序的程序比看起来更难。

标签: java arrays generics reflection instantiation


【解决方案1】:

作为回报,我必须问一个问题:您的GenSet 是“已选中”还是“未选中”? 这是什么意思?

  • 选中强类型GenSet 明确知道它包含什么类型的对象(即,它的构造函数是用 Class&lt;E&gt; 参数显式调用的,并且当传递非 E 类型的参数时,方法将抛出异常。参见 Collections.checkedCollection

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

    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;
        }
    }
    

    注意数组的组件类型应该是type参数的erasure

    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 中一个已知且有意为之的泛型弱点:它是使用擦除实现的,因此“泛型”类不知道它们在运行时使用什么类型参数创建,因此不能除非实现了某种显式机制(类型检查),否则提供类型安全。

【讨论】:

  • 性能方面的最佳选择是什么?我需要经常从这个数组中获取元素(在一个循环中)。所以一个集合可能会更慢,但是这两个中哪一个是最快的?
  • 如果泛型类型是有界的,则后备数组应该是有界类型。
  • @AaronDigulla 只是为了澄清这不是赋值,而是局部变量的初始化。您不能注释表达式/语句。
  • @Varkhan 有没有办法从类实现中调整这些数组的大小。例如,如果我想在像 ArrayList 这样的溢出后调整大小。我查了他们有Object[] EMPTY_ELEMENTDATA = {} 用于存储的ArrayList 的实现。我可以在不知道使用泛型的类型的情况下使用这种机制来调整大小吗?
  • 对于那些想要使用泛型类型(这是我一直在寻找的)方法的人,使用这个:public void &lt;T&gt; T[] newArray(Class&lt;T&gt; type, int length) { ... }
【解决方案2】:

你可以这样做:

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

这是在Effective Java 中实现泛型集合的建议方法之一;第 26 项。没有类型错误,无需重复转换数组。 但是这会触发警告,因为它具有潜在的危险性,应谨慎使用。如 cmets 中所述,此 Object[] 现在伪装成我们的 E[] 类型,如果使用不安全,可能会导致意外错误或 ClassCastExceptions。

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


值得一提的是,如果您使用的是泛型,那么在使用Lists 而不是数组时,只要有可能,您就会更快乐地工作。当然,有时您别无选择,但使用集合框架要强大得多。

【讨论】:

  • 如果数组被视为任何类型的类型化数组,例如上述test() 方法中的String[] s=b;,这将不起作用。那是因为 E 的数组不是真的,它是 Object[]。如果您愿意,这很重要,例如List&lt;String&gt;[] - 你不能为此使用Object[],你必须专门有一个List[]。这就是为什么您需要使用反射的 Class> 数组创建。
  • 极端情况/问题是,例如,当internalArray 键入为E[] 时,public E[] toArray() { return (E[])internalArray.clone(); },因此实际上是Object[]。这在运行时会因类型转换异常而失败,因为无法将 Object[] 分配给恰好是 E 的任何类型的数组。
  • 基本上,只要您不返回数组或传递数组或将其存储在需要特定类型数组的类之外的某个位置,这种方法就可以工作。只要你在课堂上就可以了,因为 E 被删除了。这是“危险的”,因为如果您尝试退回它或其他东西,您不会收到不安全的警告。但如果你小心,它就会起作用。
  • 相当安全。在E[] b = (E[])new Object[1]; 中,您可以清楚地看到创建数组的唯一引用是bb 的类型是E[]。因此,您不会有意外通过不同类型的不同变量访问同一个数组的危险。相反,如果你有Object[] a = new Object[1]; E[]b = (E[])a; ,那么你需要对你如何使用a保持偏执。
  • 至少在 Java 1.6 中,这会产生一个警告:“Unchecked cast from Object[] to T[]”
【解决方案3】:

以下是如何使用泛型来获取您正在寻找的类型的数组,同时保持类型安全(与其他答案相反,这将返回一个 Object 数组或导致编译时出现警告时间):

import java.lang.reflect.Array;  

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

    public GenSet(Class<E[]> clazz, int length) {  
        a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));  
    }  

    public static void main(String[] args) {  
        GenSet<String> foo = new GenSet<String>(String[].class, 1);  
        String[] bar = foo.a;  
        foo.a[0] = "xyzzy";  
        String baz = foo.a[0];  
    }  
}

编译时没有警告,正如您在main 中看到的那样,对于您声明GenSet 实例的任何类型,您都可以将a 分配给该类型的数组,并且您可以分配一个元素从a 到该类型的变量,这意味着数组和数组中的值是正确的类型。

正如Java Tutorials 中所讨论的,它通过使用类文字作为运行时类型标记来工作。编译器将类文字视为java.lang.Class 的实例。要使用它,只需在类名称后面加上.class。因此,String.class 充当代表 String 类的 Class 对象。这也适用于接口、枚举、任意维数组(例如String[].class)、原语(例如int.class)和关键字void(例如void.class)。

Class本身是泛型的(声明为Class&lt;T&gt;,其中T代表Class对象所代表的类型),也就是说String.class的类型是Class&lt;String&gt;

因此,每当您调用GenSet 的构造函数时,您都会为第一个参数传入一个类文字,该参数表示GenSet 实例的声明类型的数组(例如String[].class 用于GenSet&lt;String&gt;)。请注意,您将无法获得基元数组,因为基元不能用于类型变量。

在构造函数内部,调用cast 方法会将传递的Object 参数强制转换为由调用该方法的Class 对象表示的类。在java.lang.reflect.Array 中调用静态方法newInstanceObject 的形式返回一个数组,该数组的类型由作为第一个参数传递的Class 对象表示,长度由作为第二个参数传递的int 指定。调用方法getComponentType 返回一个Class 对象,该对象表示由调用该方法的Class 对象表示的数组的组件类型(例如,String.class 用于String[].classnull 如果Class对象不代表数组)。

最后一句话并不完全准确。调用String[].class.getComponentType() 返回一个Class 对象,表示类String,但它的类型是Class&lt;?&gt;,而不是Class&lt;String&gt;,这就是为什么您不能执行以下操作的原因。

String foo = String[].class.getComponentType().cast("bar"); // won't compile

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

关于 Joachim Sauer 对 this answer 的评论(我自己没有足够的声誉来评论它),使用强制转换为 T[] 的示例将导致警告,因为编译器无法保证类型安全那个案例。


编辑 Ingo 的 cmets:

public static <T> T[] newArray(Class<T[]> type, int size) {
   return type.cast(Array.newInstance(type.getComponentType(), size));
}

【讨论】:

  • 这个没用,只是写new String[...]的复杂方式。但是真正需要的是诸如 public static T[] newArray(int size) { ... } 之类的东西,而这在 java noir 中根本不存在,它可以用反射来模拟 - 原因是关于如何实例化的泛型类型在运行时不可用。
  • @Ingo 你在说什么?我的代码可用于创建任何类型的数组。
  • @Charlatan:当然可以,但是 new [] 也可以。问题是:谁知道类型和时间。因此,如果你只有一个泛型类型,你就不能。
  • 我不怀疑。关键是,您不会在运行时获得泛型 X 的 Class 对象。
  • 差不多。我承认这比使用 new[] 所能实现的要多。在实践中,这几乎总是可以完成这项工作。然而,仍然不可能,例如,编写一个用 E 参数化的容器类,它有一个方法 E[] toArray() 并且确实返回一个真正的 E[] 数组。只有当集合中至少有一个 E 对象时,才能应用您的代码。所以,一个通用的解决方案是不可能的。
【解决方案4】:

这是唯一类型安全的答案

E[] a;

a = newArray(size);

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

【讨论】:

  • 我必须查一下,但是是的,Arrays#copyOf() 的第二个“长度”参数与作为第一个参数提供的数组的长度无关。这很聪明,尽管它确实支付了调用Math#min()System#arrayCopy() 的费用,这两者都不是完成这项工作所必需的。 docs.oracle.com/javase/7/docs/api/java/util/…
  • 如果E 是一个类型变量,这将不起作用。当E 是类型变量时,可变参数创建E 的擦除数组,使其与(E[])new Object[n] 没有太大区别。请参阅http://ideone.com/T8xF91。它绝不比任何其他答案都更安全。
  • @Radiodef - 该解决方案在编译时可证明是类型安全的。请注意,擦除并不完全是语言规范的一部分;规范是精心编写的,以便我们将来可以进行全面的具体化 - 然后这个解决方案也可以在运行时完美运行,这与其他解决方案不同。
  • @Radiodef - 禁止创建泛型数组是否是个好主意值得商榷。无论如何,该语言确实留下了后门 - vararg 需要创建通用数组。就好像语言允许new E[] 一样好。您在示例中显示的问题是一般擦除问题,并非此问题和此答案所独有。
  • @Radiodef - 存在一些差异。该解决方案的正确性由编译器检查;它不依赖于强制施法的人类推理。对于这个特定问题,差异并不显着。有些人只是喜欢有点花哨,仅此而已。如果有人被 OP 的措辞误导了,你们的 cmets 和我的都会澄清。
【解决方案5】:

要扩展到更多维度,只需将[] 和维度参数添加到newInstance()T 是类型参数,clsClass&lt;T&gt;d1d5 是整数):

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()

【讨论】:

  • +1 有关于多维数组创建的问题作为这篇文章的骗子而被关闭 - 但没有答案专门解决这个问题。
  • @JordanC 也许;尽管在精神上与stackoverflow.com/a/5671304/616460 相同;明天我会考虑最好的处理方式。我困了。
【解决方案6】:

在 Java 8 中,我们可以使用 lambda 或方法引用来创建一种通用数组。这类似于反射方法(传递Class),但这里我们不使用反射。

@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);
    }
}

例如,这是&lt;A&gt; A[] Stream.toArray(IntFunction&lt;A[]&gt;)使用的。

这个可以在 Java 8 之前也可以使用匿名类来完成,但是比较麻烦。

【讨论】:

  • 你真的不需要像ArraySupplier这样的特殊接口,你可以将构造函数声明为GenSet(Supplier&lt;E[]&gt; supplier) { ...并使用与你相同的行调用它。
  • @Lii 和我的例子一样,应该是IntFunction&lt;E[]&gt;,但确实如此。
【解决方案7】:

这在Effective Java, 2nd Edition 的第 5 章(泛型)第 25 项中进行了介绍...首选列表而不是数组

您的代码将起作用,尽管它会生成一个未经检查的警告(您可以使用以下注释来抑制它:

@SuppressWarnings({"unchecked"})

但是,使用列表而不是数组可能会更好。

the OpenJDK project site 上有一个关于这个错误/功能的有趣讨论。

【讨论】:

    【解决方案8】:

    您不需要将 Class 参数传递给构造函数。 试试这个。

    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]
    

    【讨论】:

      【解决方案9】:

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

      公共Stack(Class&lt;T&gt; clazz,int capacity) 构造函数要求您在运行时传递一个Class 对象,这意味着类信息在运行时可供需要它的代码使用。 Class&lt;T&gt; 形式意味着编译器将检查您传递的 Class 对象是否正是 T 类型的 Class 对象。不是 T 的子类,不是 T 的超类,而是 T。

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

      【讨论】:

        【解决方案10】:

        虽然线程已经死了,但我想提请您注意这一点。

        泛型用于编译期间的类型检查。因此,目的是检查

        • 进来的就是你需要的。
        • 您返回的就是消费者需要的。

        检查一下:

        在编写泛型类时不要担心类型转换警告;用的时候放心。

        【讨论】:

          【解决方案11】:

          这个解决方案怎么样?

          @SafeVarargs
          public static <T> T[] toGenericArray(T ... elems) {
              return elems;
          }
          

          它很有效,看起来太简单了。有什么缺点吗?

          【讨论】:

          • 整洁,但仅在您将其称为“手动”时才有效,即单独传递元素。如果您无法创建T[] 的新实例,则无法以编程方式构建T[] elems 以传递给函数。如果可以的话,你就不需要这个函数了。
          【解决方案12】:

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

          【讨论】:

          • 第二个例子(使用 Array.newInstance())实际上是类型安全的。这是可能的,因为 Class 对象的类型 T 需要与数组的 T 匹配。它基本上强制您提供 Java 运行时为泛型丢弃的信息。
          【解决方案13】:

          也看看这段代码:

          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;
          }
          

          它将任何类型的对象列表转换为相同类型的数组。

          【讨论】:

          • 是的,您返回 null,这不是预期的空数组。这是你能做的最好的,但并不理想。
          • 如果List 中有不止一种类型的对象,这也可能会失败,例如toArray(Arrays.asList("abc", new Object())) 将抛出 ArrayStoreException
          • 我使用了精简版;我能够使用的第一件事是有效的,尽管我承认我没有尝试一些更复杂的解决方案。为了避免for 循环和其他循环,我使用了Arrays.fill(res, obj);,因为我希望每个索引都具有相同的值。
          【解决方案14】:

          我找到了一种适合我的快速简便的方法。请注意,我只在 Java JDK 8 上使用过它。我不知道它是否适用于以前的版本。

          虽然我们无法实例化特定类型参数的泛型数组,但我们可以将已创建的数组传递给泛型类构造函数。

          class GenArray <T> {
              private T theArray[]; // reference array
          
              // ...
          
              GenArray(T[] arr) {
                  theArray = arr;
              }
          
              // Do whatever with the array...
          }
          

          现在在 main 中我们可以像这样创建数组:

          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
          
                  // ...
          
              }
          }
          

          为了让您的数组更加灵活,您可以使用链表,例如。 Java.util.ArrayList 类中的 ArrayList 和其他方法。

          【讨论】:

            【解决方案15】:

            传递值列表...

            public <T> T[] array(T... values) {
                return values;
            }
            

            【讨论】:

              【解决方案16】:

              我编写了这段代码 sn-p 以反射性地实例化一个通过简单的自动化测试实用程序传递的类。

              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);
                  }
              

              用于数组初始化 where Array.newInstance(数组的类,数组的大小)。类既可以是原始(int.class)也可以是对象(Integer.class)。

              BeanUtils 是 Spring 的一部分。

              【讨论】:

                【解决方案17】:

                其他人建议的强制转换对我不起作用,抛出非法转换异常。

                但是,这种隐式转换效果很好:

                Item<K>[] array = new Item[SIZE];
                

                其中 Item 是我定义的包含该成员的类:

                private K value;
                

                这样你得到一个类型为 K 的数组(如果项目只有值)或你想在类 Item 中定义的任何泛型类型。

                【讨论】:

                  【解决方案18】:

                  实际上更简单的方法是创建一个对象数组并将其转换为您想要的类型,如下例所示:

                  T[] array = (T[])new Object[SIZE];
                  

                  其中SIZE 是一个常量,T 是一个类型标识符

                  【讨论】:

                  • 给出 java.lang.ClassCastException: [Ljava.lang.Object;无法转换为 [Ljava.util.concurrent.TransferQueue;
                  【解决方案19】:

                  没有其他人回答您发布的示例中发生了什么的问题。

                  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;
                  }
                  

                  正如其他人所说,泛型在编译期间被“擦除”。所以在运行时,一个泛型的实例不知道它的组件类型是什么。这样做的原因是历史原因,Sun 希望在不破坏现有接口(源代码和二进制文件)的情况下添加泛型。

                  另一方面,数组确实在运行时知道它们的组件类型。

                  这个例子通过让调用构造函数(它知道类型)的代码传递一个参数来告诉类所需的类型来解决这个问题。

                  所以应用程序会用类似的东西构造类

                  Stack<foo> = new Stack<foo>(foo.class,50)
                  

                  并且构造函数现在(在运行时)知道组件类型是什么,并且可以使用该信息通过反射 API 构造数组。

                  Array.newInstance(clazz, capacity);
                  

                  最后我们有一个类型转换,因为编译器无法知道Array#newInstance() 返回的数组是正确的类型(即使我们知道)。

                  这种风格有点难看,但它有时可能是创建泛型类型的最不坏的解决方案,无论出于何种原因(创建数组或创建其组件类型的实例等)都需要在运行时知道其组件类型.

                  【讨论】:

                    【解决方案20】:

                    我找到了解决这个问题的方法。

                    下面的行抛出泛型数组创建错误

                    List<Person>[] personLists=new ArrayList<Person>()[10];
                    

                    但是,如果我将 List&lt;Person&gt; 封装在一个单独的类中,它就可以工作。

                    import java.util.ArrayList;
                    import java.util.List;
                    
                    
                    public class PersonList {
                    
                        List<Person> people;
                    
                        public PersonList()
                        {
                            people=new ArrayList<Person>();
                        }
                    }
                    

                    您可以通过 getter 公开 PersonList 类中的人员。下面的行会给你一个数组,每个元素都有一个List&lt;Person&gt;。换句话说,List&lt;Person&gt; 的数组。

                    PersonList[] personLists=new PersonList[10];
                    

                    我需要在我正在处理的一些代码中使用类似的东西,这就是我为了让它工作而做的。到目前为止没有问题。

                    【讨论】:

                      【解决方案21】:

                      您可以创建一个 Object 数组并将其转换为 E。是的,这不是很干净的方法,但至少应该可以工作。

                      【讨论】:

                      • "我们正在寻找能够提供一些解释和背景的长答案。不要只给出一个单一的答案;解释为什么你的答案是正确的,最好是引用。没有解释的答案可能是已删除。”
                      • BUt 在某些情况下不起作用,例如如果您的泛型类想要实现 Comparable 接口。
                      • 欢迎来到七年前,我想。
                      • 如果您尝试将数组从泛型代码返回给非泛型调用者,这将不起作用。会有一个令人头疼的 classcastexception。
                      【解决方案22】:

                      试试这个。

                      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>();
                              }
                          }
                      }
                      

                      【讨论】:

                      • 我无法让你的代码运行,你的Element 类是从哪里来的?
                      【解决方案23】:

                      一个简单但复杂的解决方法是在你的主类中嵌套第二个“持有者”类,并用它来保存你的数据。

                      public class Whatever<Thing>{
                          private class Holder<OtherThing>{
                              OtherThing thing;
                          }
                          public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
                      }
                      

                      【讨论】:

                      • 这实际上不起作用。 new Holder&lt;Thing&gt;[10] 是一个通用数组创建。
                      【解决方案24】:

                      也许与这个问题无关,但是当我收到“generic array creation”使用错误时

                      Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];
                      

                      我通过@SuppressWarnings({"unchecked"}) 发现了以下作品(并为我工作):

                       Tuple<Long, String>[] tupleArray = new Tuple[10];
                      

                      【讨论】:

                      • 是的,这并不完全相关,但根源于相同的问题(擦除,数组协方差)。下面是一个关于创建参数化类型数组的帖子示例:stackoverflow.com/questions/9542076/…
                      【解决方案25】:

                      我想知道这段代码是否会创建一个有效的泛型数组?

                      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;
                      }
                      

                      编辑:如果您需要的大小已知且很小,也许创建这样一个数组的另一种方法是简单地将所需数量的“null”输入 zeroArray 命令?

                      虽然显然这不像使用 createArray 代码那样通用。

                      【讨论】:

                      • 不,这不起作用。当T 是类型变量时,可变参数创建T 的擦除,即zeroArray 返回Object[]。见http://ideone.com/T8xF91
                      【解决方案26】:

                      你可以使用演员表:

                      public class GenSet<Item> {
                          private Item[] a;
                      
                          public GenSet(int s) {
                              a = (Item[]) new Object[s];
                          }
                      }
                      

                      【讨论】:

                      • 如果你要建议这个,你真的需要解释它的局限性。切勿将a 暴露在课外!
                      【解决方案27】:

                      我实际上找到了一个非常独特的解决方案来绕过无法启动通用数组。您需要做的是创建一个接收通用变量 T 的类,如下所示:

                      class GenericInvoker <T> {
                          T variable;
                          public GenericInvoker(T variable){
                              this.variable = variable;
                          }
                      }
                      

                      然后在你的数组类中让它像这样开始:

                      GenericInvoker<T>[] array;
                      public MyArray(){
                          array = new GenericInvoker[];
                      }
                      

                      启动new Generic Invoker[] 会导致未选中的问题,但实际上不应该有任何问题。

                      要从数组中获取,您应该像这样调用 array[i].variable:

                      public T get(int index){
                          return array[index].variable;
                      }
                      

                      其余的,比如调整数组大小可以用 Arrays.copyOf() 来完成,如下所示:

                      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++;
                      }
                      

                      【讨论】:

                      • 问题是关于创建泛型类型参数T 的类型的数组,而不是某个参数化类型的数组。
                      • 它完成了同样的任务,并且不需要你推入一个类,使你的自定义集合更易于使用。
                      • 什么任务?这实际上是一项不同的任务:参数化类型的数组与泛型类型参数的数组。
                      • 它允许你从泛型类型创建一个数组?最初的问题是使用泛型类型初始化数组,使用我的方法可以让您不必让用户推入类或给出未经检查的错误,例如尝试将对象转换为字符串。就像寒意一样,我在我所做的事情上并不是最擅长的,而且我没有上过编程学校,但我认为我仍然应该得到一点意见,而不是被互联网上的其他孩子指责。
                      • 我同意 Sotiros。有两种方法可以考虑答案。它要么是对不同问题的回答,要么是试图概括问题。两者都是错误的/没有帮助。正在寻找有关如何实现“通用数组”类的指导的人在阅读问题标题时会/停止阅读。当他们找到一个有 30 个答案的 Q 时,他们极不可能滚动到最后并阅读一个 SO 新手的零票答案。
                      【解决方案28】:

                      根据 vnportnoy 的语法

                      GenSet<Integer> intSet[] = new GenSet[3];
                      

                      创建一个空引用数组,填充为

                      for (int i = 0; i < 3; i++)
                      {
                         intSet[i] = new GenSet<Integer>();
                      }
                      

                      这是类型安全的。

                      【讨论】:

                        【解决方案29】:
                        private E a[];
                        private int size;
                        
                        public GenSet(int elem)
                        {
                            size = elem;
                            a = (E[]) new E[size];
                        }
                        

                        【讨论】:

                        • 您应该始终在您的代码中添加解释,并解释为什么它可以解决原始发布的问题。
                        【解决方案30】:

                        Java 中不允许创建通用数组,但您可以这样做

                        class Stack<T> {
                        private final T[] array;
                        public Stack(int capacity) {
                            array = (T[]) new Object[capacity];
                         }
                        }
                        

                        【讨论】:

                          猜你喜欢
                          相关资源
                          最近更新 更多