Java 中泛型数组的最简单替代方案是什么?

What is the easiest alternative to a generic array in Java?

提问人: 提问时间:12/21/2008 更新时间:8/9/2013 访问量:9723

问:

假设我有这个:

class test<T>
{
    private T[] elements;
    private int size;
    public test(int size)
    {
        this.size = size;
        elements = new T[this.size];
    }
}

这似乎是不可能的,因为编译器一旦尝试替换泛型代码或其他东西,就不知道要调用什么构造函数。我想知道的是,我该怎么做?我认为这是可能的,因为它在 C++ 中很容易完成。

编辑:对不起,我忘记了元素声明中的[]。

Java 数组泛

评论


答:

0赞 mat 12/21/2008 #1

我可能错了,但你的声明似乎很奇怪,你不应该有:

private T[] elements;
14赞 Johannes Schaub - litb 12/21/2008 #2

问题在于,由于泛型类型参数是由编译器转换为的(称为类型擦除),因此您实际上创建了一个 .您可以做的是向函数提供:TObjectObjectClass<T>

class test<T>
{
    private T[] elements;
    private int size;
    public test(Class<T> type, int size)
    {
        this.size = size;
        elements = (T[]) Array. newInstance(type, size);
    }
}

你会在这里找到更好的解释: Angelika Langer - 我可以创建一个组件类型为类型参数的数组吗?

8赞 Dan Dyer 12/21/2008 #3

泛型数组的最简单替代方法是使用列表。

评论

0赞 Alex Miller 12/21/2008
我同意 - 出于多种原因,列表通常是更好的选择。事实上,有些人会建议数组应该被视为 Java 5+ 中不推荐使用的语言部分。我不是说我是其中之一......但你会发现这种观点。
0赞 Tom Hawtin - tackline 12/21/2008
数组对基元类型仍然有效。但对于参考类型来说,它几乎总是不恰当的优化。
2赞 newacct 5/3/2009 #4

有两种解决方案不需要传入类对象。在这两种情况下,我都假设,既然它是一个私有变量,你是唯一一个把东西放进去的人,而外界看不到它。

一种是只使用一个普通的Object数组。缺点是你将失去泛型的好处,你将不得不从中转换出来的东西,这使得代码更加丑陋。但是,由于您是唯一将东西放入其中的人,因此演员应该始终是安全的。外面不需要知道发生了什么。Object[]

其他选项是一种黑客攻击。创建一个 ,然后将其“强制”到 。从技术上讲,这是非法的,因为 和 是不同的运行时类型,前者不能强制转换为后者。但是,只要你不将数组传出类(例如返回它或其他东西),那么它就不会引起任何问题,因为无论如何都会被擦除,并且实际上不会执行任何强制转换。然后,您可以在类中获得泛型的好处,以便更轻松地编码,但您必须非常小心,不要将该数组传递给任何实际需要 的人,因为它不是。Object[]T[]Object[]T[]T[]Object[]T[]

3赞 Bob Cross 5/3/2009 #5

出于多种原因,我倾向于避免使用基本数组,并尽可能使用 ArrayLists(或类似的 Lists)。最重要的两个是:

  1. 我不需要秃头数组的语义。括号符号(即指针算术的简写)没有什么能让我的生活更轻松。

  2. 如果我使用列表,我会根据需要将自己设置为使用更复杂的并发结构。例如,将 ArrayList 替换为 CopyOnWriteArrayList 非常容易。

因此,使用泛型 ArrayList 编写示例将如下所示:

class test<T> {
    /** It's not strictly necessary to make this a List vs. an ArrayList
        You decide what interface you really need to expose internally. */
    private List<T> elements;
    /** This parameter isn't necessary with a List.  You can always call the
        size() method instead. */
    private int size;
    public test(int size) {
        this.size = size;
        elements = new ArrayList<T>();
        // Note that the next line isn't strictly necessary.
        // An ArrayList can be dynamically resized without any real effort on your part.
        elements.ensureCapacity(size); 
    }
}
0赞 Peter Lawrey 5/3/2009 #6

size 字段看起来确实是多余的。你可以只用 elements.length 而不是 size。

class Test<T> {
    private final T[] elements;
    public test(int size) {
        elements = (T[]) new Object[size];
    }
}

或者,可以将 Test 类替换为 ArrayList。即完全放弃类。

评论

0赞 pdeva 5/3/2009
这将引发异常,因为新数组不是 T[] 类型。
0赞 newacct 5/3/2009
@pdeva:不,不会;只要它停留在类 Test 中,它就不会抛出任何异常,因为 T[] 的擦除是 Object[];但你必须小心不要让它离开课堂
0赞 MatheusJardimB 8/9/2013 #7

另请查看以下代码:

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

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