通用列表数组

发布于 2024-12-10 11:26:08 字数 575 浏览 0 评论 0原文

我正在使用泛型和数组,似乎下面的代码编译得很好,

ArrayList<Key> a = new ArrayList<Key>();

但是编译器抱怨这个,

ArrayList<Key>[] a = new ArrayList<Key>[10];

通过阅读 stackoverflow 中的帖子,我有点明白这是由于类型擦除造成的,我可以通过使用来修复它,

ArrayList<Key>[] a = (ArrayList<Key> []) new ArrayList[10];

或者list of list

ArrayList<ArrayList<Key>> b = new ArrayList<ArrayList<Key>>();

但我无法弄清楚幕后的原因。特别是,为什么第二个是非法的,因为第一个是完全可以的。以及为什么编译器不会抱怨列表的列表。

I am playing with Generic and arrays, it seems the following code compiles fine,

ArrayList<Key> a = new ArrayList<Key>();

But the compiler complains about this one,

ArrayList<Key>[] a = new ArrayList<Key>[10];

By reading post in stackoverflow, I sort of understand that this is due to Type Erasure and I can fix it by using,

ArrayList<Key>[] a = (ArrayList<Key> []) new ArrayList[10];

or list of list

ArrayList<ArrayList<Key>> b = new ArrayList<ArrayList<Key>>();

But I can't figure out the reason behind the scene. Especially, why the second one is illegal given the first one is perfectly OK. And why the compiler does not complain about the list of list.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

半边脸i 2024-12-17 11:26:08

您不能拥有数组,因为数组需要原始类型。您在第二个实例中对其进行类型转换,这使其适合定义的类型,因此是合法的(但是,它不可能推断)。列表的列表是合法的,因为 ArrayList 不是数组。

阅读官方教程中的第 7.3 章(第 15 页)了解更多信息有关于此的详细信息。

数组对象的组件类型不能是类型变量或
参数化类型,除非它是(无界)通配符类型。您可以
声明其元素类型为类型变量或
参数化类型,但不是数组对象。
当然,这很烦人。此限制对于避免以下情况是必要的:

List[] lsa = new List[10]; // 实际上不允许
对象 o = lsa;
对象[] oa = (对象[]) o;
列表<整数> li = new ArrayList();
li.add(new Integer(3));
oa[1] = li; // 不健全,但通过了运行时存储检查
字符串 s = lsa[1].get(0); // 运行时错误 - ClassCastException

如果允许参数化类型的数组,上面的示例将
编译时没有任何未经检查的警告,但在运行时失败。

教程接着说以下内容:

由于类型变量在运行时不存在,因此无法确定类型变量是什么
实际的数组类型是。
解决这些限制的方法是使用类文字作为运行时
类型标记

You can't have an array, because an array requires a raw type. You typecast it in the second instance, which makes it fit the defined type, and is therefore legal (however, this is impossible for it to infer). The list of list is legal as ArrayList isn't an array.

Read chapter 7.3 (page 15) in the official tutorial for more details on this.

The component type of an array object may not be a type variable or a
parameterized type, unless it is an (unbounded) wildcard type.You can
declare array types whose element type is a type variable or a
parameterized type, but not array objects.
This is annoying, to be sure. This restriction is necessary to avoid situations like:

List<String>[] lsa = new List<String>[10]; // not really allowed
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // unsound, but passes run time store check
String s = lsa[1].get(0); // run-time error - ClassCastException

If arrays of parameterized type were allowed, the example above would
compile without any unchecked warnings, and yet fail at run-time.

The tutorial then goes on to say the following:

Since type variables don’t exist at run time, there is no way to determine what the
actual array type would be.
The way to work around these kinds of limitations is to use class literals as run time
type tokens

不醒的梦 2024-12-17 11:26:08

数组是穷人的泛型;对于真正的泛型,人们应该避免使用数组,尽管并不总是可行。

数组是协变的,泛型是不变的;与擦除相结合,事情就不太适合,正如克里斯回答中的例子所示。

不过我认为可以放宽规范以允许创建通用数组 - 那里确实没有问题。当上施法阵时危险就来了;此时编译器警告就足够了。

实际上Java确实为可变参数方法创建了泛型数组,所以这有点虚伪。

以下是利用这一事实的实用方法

@SafeVarargs
static <E> E[] arrayLiteral(E... array)
{
    return array;
}

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

// usage

    List<String>[] array1 = arrayLiteral(list, list);

    List<String>[] array2 = newArray(10);

Array was poor man's generics; with real generics, one should avoid arrays, though not always possible.

Arrays are covariant, generics are invariant; combined with erasure, things just don't fit very well, as illustrated by the example in Chris's answer.

However I think it is possible to relax the spec to allow generic array creation - there's really no problem there. The danger comes when up casting the array; a compiler warning at that point is enough.

Actually Java does create generic arrays for vararg methods, so it's a little hypocritical.

Here are utility methods taking advantage of that fact

@SafeVarargs
static <E> E[] arrayLiteral(E... array)
{
    return array;
}

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

// usage

    List<String>[] array1 = arrayLiteral(list, list);

    List<String>[] array2 = newArray(10);
爱本泡沫多脆弱 2024-12-17 11:26:08

我自己也有一个类似的问题 - FWIW,我发现答案没有说服力。最详细答案(参考pdf参考)的相关部分是这样的:

数组对象的组件类型不能是类型变量或
参数化类型,除非它是(无界)通配符类型。您可以
声明其元素类型为类型变量或
参数化类型,但不是数组对象。这很烦人,要
当然。此限制对于避免类似情况是必要的

       List<String>[] lsa = new List<String>[10]; // not really allowed
       Object o = lsa;
       Object[] oa = (Object[]) o;
       List<Integer> li = new ArrayList<Integer>();
       li.add(new Integer(3));
       oa[1] = li; // unsound, but passes run time store check
       String s = lsa[1].get(0); // run-time error - ClassCastException

,因为我可以将 List[] 转换为 Object[],然后将不正确的内容塞入 Object[],然后通过强制转换的引用从 List 引用中错误地引用,这是不好的/不允许?但只有新的?

对我来说,仍然有点模糊的是,用 new 声明它比用法或多或少是一个问题,仍然交叉着眼睛盯着它,希望它能开始有意义,或者至少解决为一个漂亮的 3D 图像。

I had a similar question myself - FWIW, I didn't find the answers persuasive. The pertinent section from the most detailed answer (referring to the pdf reference) is this:

The component type of an array object may not be a type variable or a
parameterized type, unless it is an (unbounded) wildcard type.You can
declare array types whose element type is a type variable or a
parameterized type, but not array objects. This is annoying, to be
sure. This restriction is necessary to avoid situations like

       List<String>[] lsa = new List<String>[10]; // not really allowed
       Object o = lsa;
       Object[] oa = (Object[]) o;
       List<Integer> li = new ArrayList<Integer>();
       li.add(new Integer(3));
       oa[1] = li; // unsound, but passes run time store check
       String s = lsa[1].get(0); // run-time error - ClassCastException

So because I can cat the List[] to Object[], then shove something incorrect into the Object[], then refer to incorrectly from the List reference, through the casted ref, this is bad/disallowed? But only with new?

It's still more than a bit obscure to me how declaring this with new is any more or less of a problem than the usage, still crossing my eyes staring at it in the hope that it will start to make sense, or at least resolve into a nice 3d image.

澉约 2024-12-17 11:26:08

创建泛型数组不是类型安全的(请参阅 Joshua Bloch 的《Effective Java - 第二版》中的“条目 25:优先选择列表而不是数组”)。

使用:

 List<List<Key>> b = new ArrayList<List<Key>>(10);

或者使用 Java SE 7:

 List<List<Key>> b = new ArrayList<>(10);

Creating generic arrays isn't type-safe (see "Item 25: Prefer lists to arrays" of "Effective Java - second edition" by Joshua Bloch).

Use:

 List<List<Key>> b = new ArrayList<List<Key>>(10);

Or with Java SE 7:

 List<List<Key>> b = new ArrayList<>(10);
秋叶绚丽 2024-12-17 11:26:08

这些数组允许逃避类型检查(如克里斯的回答所示)。因此,您的代码可以通过所有编译器检查(编译器不会发出“未经检查”的警告),但在运行时会失败并出现 ClassCastException。
禁止这种构造会给开发人员带来问题,因此确实会出现警告。

The arrays allow to escape type checks (as illustrated in the Chris's answer). So, you could have a code which passes all compiler checks (no "unchecked" warnings from compiler), but fail at run time with ClassCastException.
Forbidding this construction raises the problem for a developer, so warnings do appear.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文