ChatGPT解决这个技术问题 Extra ChatGPT

如何在 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;
}

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

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

R
Radiodef

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

检查:强类型。 GenSet 明确知道它包含什么类型的对象(即,它的构造函数是用 Class 参数显式调用的,并且当传递非 E 类型的参数时,方法将抛出异常。请参阅 Collections.checkedCollection。-> 在在这种情况下,你应该这样写: public class GenSet { private E[] a; public GenSet(Class c, int s) { // 使用 Array 本机方法创建数组 // 只有在运行时 @SuppressWarnings("unchecked") final E[] a = (E[]) Array.newInstance(c, s); this.a = a; } E get(int i) { return a[i]; } }

未选中:弱类型。实际上没有对作为参数传递的任何对象进行类型检查。 -> 在这种情况下,你应该写 public class GenSet { private Object[] a;公共 GenSet(int s) { a = new Object[s]; } E get(int i) { @SuppressWarnings("unchecked") final E e = (E) a[i];返回 e; } } 注意数组的组件类型应该是类型参数的擦除: public class GenSet { // E 的上限为 Foo private Foo[] a; // E 擦除到 Foo,所以使用 Foo[] public GenSet(int s) { a = new Foo[s]; } ... }

所有这些都源于 Java 中泛型的一个已知且有意的弱点:它是使用擦除实现的,因此“泛型”类不知道它们在运行时使用什么类型参数创建,因此无法提供类型-除非实现了某种明确的机制(类型检查),否则安全。


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

你可以这样做:

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

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

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

值得一提的是,如果您使用泛型,只要有可能,使用 List 而不是数组会更愉快。当然,有时您别无选择,但使用集合框架要健壮得多。


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

以下是如何使用泛型来获取您正在寻找的类型的数组,同时保持类型安全(与其他答案相反,这将给您返回一个 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 分配给该类型的数组,并且您可以从 {3 } 到该类型的变量,这意味着数组和数组中的值是正确的类型。

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

Class 本身是通用的(声明为 Class<T>,其中 T 代表 Class 对象所代表的类型),这意味着 String.class 的类型是 Class<String>

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

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

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

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

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

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

编辑关于 Ingo 的评论:

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 对象时,才能应用您的代码。所以,一个通用的解决方案是不可能的。
R
Radiodef

这是唯一类型安全的答案

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 的措辞误导,您的评论和我的评论都会澄清这一点。
n
naXa stands with Ukraine

要扩展到更多维度,只需将 [] 和维度参数添加到 newInstance()T 是类型参数,clsClass<T>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 有关于多维数组创建的问题作为这篇文章的欺骗而被关闭 - 但没有答案专门解决这个问题。
R
Radiodef

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

例如,这由 <A> A[] Stream.toArray(IntFunction<A[]>) 使用。

这也可以在 Java 8 之前使用匿名类完成,但更麻烦。


您实际上并不需要像 ArraySupplier 这样的特殊接口,您可以将构造函数声明为 GenSet(Supplier<E[]> supplier) { ... 并使用与您相同的行调用它。
@Lii 与我的示例相同,它将是 IntFunction<E[]>,但是是的,这是真的。
J
Jeff Olson

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

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

@SuppressWarnings({"unchecked"})

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

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


佚名

您不需要将 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]

B
Bill Michell

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

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

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


A
Arvind Kumar Avinash

尽管线程已死,但我想提请您注意这一点。

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

进来的就是你需要的。

你返回的就是消费者需要的。

检查这个:

https://i.stack.imgur.com/fCXXe.jpg

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


B
Benjamin M

这个解决方案怎么样?

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

它有效,看起来太简单了,不可能是真的。有什么缺点吗?


整洁,但只有在您将其称为“手动”时才有效,即单独传递元素。如果无法创建 T[] 的新实例,则无法以编程方式构建 T[] elems 以传递给函数。如果可以,您将不需要该功能。
O
Ola Bini

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


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

另请查看此代码:

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);,因为我希望每个索引都具有相同的值。
K
KeyC0de

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

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

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

        // ...

    }
}

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


R
Rodrigo Asensio

传递值列表...

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

B
Bobster

我制作了这个代码片段来反射地实例化一个为简单的自动化测试实用程序传递的类。

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 的一部分。


V
Valentino

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

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

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

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

private K value;

这样,您将获得 K 类型的数组(如果该项只有值)或您希望在 Item 类中定义的任何泛型类型。


s
shruti1810

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

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

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


给出 java.lang.ClassCastException: [Ljava.lang.Object;无法转换为 [Ljava.util.concurrent.TransferQueue;
s
seh

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

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() 返回的数组是正确的类型(即使我们知道)。

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


d
developer747

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

下面的行抛出通用数组创建错误

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

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

import java.util.ArrayList;
import java.util.List;


public class PersonList {

    List<Person> people;

    public PersonList()
    {
        people=new ArrayList<Person>();
    }
}

您可以通过 getter 公开 PersonList 类中的人员。下面的行将为您提供一个数组,每个元素中都有一个 List<Person>。换句话说,List<Person> 的数组。

PersonList[] personLists=new PersonList[10];

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


I
Irfan Ul Haq

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

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

S
Sam Ginrich

根据 vnportnoy 的语法

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

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

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

这是类型安全的。


E
Esko

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


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

尝试这个。

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 类来自哪里?
S
StarMonkey

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

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

这实际上不起作用。 new Holder<Thing>[10] 是一个通用数组创建。
M
Mohsen Afshin

也许与这个问题无关,但是当我得到“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/…
C
Cambot

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

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
s
samir benzenine

您可以使用演员表:

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

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

如果你要提出这个建议,你真的需要解释它的局限性。切勿将 a 暴露在课堂外!
C
Crab Nebula

我实际上找到了一个非常独特的解决方案来绕过无法启动通用数组。您需要做的是创建一个接收泛型变量 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);
}

add 函数可以像这样添加:

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

如果您真的想包装一个固定大小的通用数组,您将有一个方法来向该数组添加数据,因此您可以在那里正确初始化数组,执行如下操作:

import java.lang.reflect.Array;

class Stack<T> {
    private T[] array = null;
    private final int capacity = 10; // fixed or pass it in the constructor
    private int pos = 0;

    public void push(T value) {
        if (value == null)
            throw new IllegalArgumentException("Stack does not accept nulls");
        if (array == null)
            array = (T[]) Array.newInstance(value.getClass(), capacity);
        // put logic: e.g.
        if(pos == capacity)
             throw new IllegalStateException("push on full stack");
        array[pos++] = value;
    }

    public T pop() throws IllegalStateException {
        if (pos == 0)
            throw new IllegalStateException("pop on empty stack");
        return array[--pos];
    }
}

在这种情况下,您使用 java.lang.reflect.Array.newInstance 创建数组,它不会是 Object[],而是真正的 T[]。您不必担心它不是最终的,因为它是在您的班级内部管理的。请注意,您需要 push() 上的非 null 对象才能获取要使用的类型,因此我添加了对您推送的数据的检查并在那里引发异常。

这仍然有些毫无意义:您通过 push 存储数据,它是保证只有 T 元素会进入的方法的签名。因此,数组是 Object[] 还是 T[] 或多或少无关紧要。