如何在 Java 中创建通用数组?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/529085/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create a generic array in Java?
提问by tatsuhirosatou
Due to the implementation of Java generics, you can't have code like this:
由于 Java 泛型的实现,你不能有这样的代码:
public class GenSet<E> {
private E a[];
public GenSet() {
a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
}
}
How can I implement this while maintaining type safety?
如何在保持类型安全的同时实现这一点?
I saw a solution on the Java forums that goes like this:
我在 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;
}
But I really don't get what's going on.
但我真的不明白发生了什么。
采纳答案by Varkhan
I have to ask a question in return: is your GenSet
"checked" or "unchecked"?
What does that mean?
作为回报,我必须问一个问题:您的GenSet
“已选中”还是“未选中”?这意味着什么?
Checked: strong typing.
GenSet
knows explicitly what type of objects it contains (i.e. its constructor was explicitly called with aClass<E>
argument, and methods will throw an exception when they are passed arguments that are not of typeE
. SeeCollections.checkedCollection
.-> in that case, you should write:
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]; } }
Unchecked: weak typing. No type checking is actually done on any of the objects passed as argument.
-> in that case, you should write
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; } }
Note that the component type of the array should be the erasureof the type parameter:
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]; } ... }
检查:强类型。
GenSet
明确知道它包含什么类型的对象(即,它的构造函数是用Class<E>
参数显式调用的,当传递的参数不是 type 时,方法将抛出异常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; } }
注意数组的组件类型应该是类型参数的擦除:
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]; } ... }
All of this results from a known, and deliberate, weakness of generics in Java: it was implemented using erasure, so "generic" classes don't know what type argument they were created with at run time, and therefore can not provide type-safety unless some explicit mechanism (type-checking) is implemented.
所有这些都源于 Java 中泛型的一个已知的、故意的弱点:它是使用擦除实现的,因此“泛型”类不知道它们在运行时创建的类型参数,因此不能提供类型-除非实现了某种明确的机制(类型检查),否则安全。
回答by Ola Bini
The example is using Java reflection to create an array. Doing this is generally not recommended, since it isn't typesafe. Instead, what you should do is just use an internal List, and avoid the array at all.
该示例使用 Java 反射来创建数组。通常不建议这样做,因为它不是类型安全的。相反,您应该做的只是使用内部列表,并完全避免使用数组。
回答by Esko
You could create an Object array and cast it to E everywhere. Yeah, it's not very clean way to do it but it should at least work.
您可以创建一个 Object 数组并将其强制转换为 E 无处不在。是的,这不是很干净的方法,但至少应该有效。
回答by Jeff Olson
This is covered in Chapter 5 (Generics) of Effective Java, 2nd Edition, item 25...Prefer lists to arrays
这在Effective Java 的第 5 章(泛型),第 2 版,第25 项...首选列表而不是数组
Your code will work, although it will generate an unchecked warning (which you could suppress with the following annotation:
您的代码将起作用,但它会生成一个未经检查的警告(您可以使用以下注释来抑制它:
@SuppressWarnings({"unchecked"})
However, it would probably be better to use a List instead of an Array.
但是,使用 List 而不是 Array 可能会更好。
There's an interesting discussion of this bug/feature on the OpenJDK project site.
在 OpenJDK 项目站点上有一个关于此错误/功能的有趣讨论。
回答by Bill Michell
Java generics work by checking types at compile time and inserting appropriate casts, but erasingthe types in the compiled files. This makes generic libraries usable by code which doesn't understand generics (which was a deliberate design decision) but which means you can't normally find out what the type is at run time.
Java 泛型通过在编译时检查类型并插入适当的强制转换来工作,但会删除编译文件中的类型。这使得不理解泛型的代码可以使用泛型库(这是一个深思熟虑的设计决定),但这意味着您通常无法在运行时找出类型是什么。
The public Stack(Class<T> clazz,int capacity)
constructor requires you to pass a Class object at run time, which means class information isavailable at runtime to code that needs it. And the Class<T>
form means that the compiler will check that the Class object you pass is precisely the Class object for type T. Not a subclass of T, not a superclass of T, but precisely T.
公共Stack(Class<T> clazz,int capacity)
构造函数需要你通过在运行时类对象,这意味着类信息是可在运行时需要它的代码。并且Class<T>
形式意味着编译器将检查您传递的 Class 对象是否正是类型 T 的 Class 对象。不是 T 的子类,不是 T 的超类,而是准确的 T。
This then means that you can create an array object of the appropriate type in your constructor, which means that the type of the objects you store in your collection will have their types checked at the point they are added to the collection.
这意味着您可以在构造函数中创建适当类型的数组对象,这意味着您存储在集合中的对象的类型将在它们添加到集合时检查它们的类型。
回答by dimo414
You can do this:
你可以这样做:
E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];
This is one of the suggested ways of implementing a generic collection in Effective Java; Item 26. No type errors, no need to cast the array repeatedly. Howeverthis triggers a warning because it is potentially dangerous, and should be used with caution. As detailed in the comments, this Object[]
is now masquerading as our E[]
type, and can cause unexpected errors or ClassCastException
s if used unsafely.
这是在Effective Java中实现泛型集合的建议方法之一;第 26 项。没有类型错误,不需要重复转换数组。 但是,这会触发警告,因为它具有潜在危险,应谨慎使用。正如评论中详述的那样,这Object[]
现在伪装成我们的E[]
类型,ClassCastException
如果使用不安全,可能会导致意外错误或s。
As a rule of thumb, this behavior is safe as long as the cast array is used internally (e.g. to back a data structure), and not returned or exposed to client code. Should you need to return an array of a generic type to other code, the reflection Array
class you mention is the right way to go.
根据经验,只要在内部使用强制转换数组(例如支持数据结构),并且不返回或暴露给客户端代码,这种行为就是安全的。如果您需要将泛型类型的数组返回给其他代码,Array
您提到的反射类是正确的方法。
Worth mentioning that wherever possible, you'll have a much happier time working with List
s rather than arrays if you're using generics. Certainly sometimes you don't have a choice, but using the collections framework is far more robust.
值得一提的是,List
如果您使用泛型,则在可能的情况下,使用s 而不是数组会更快乐。当然,有时您别无选择,但使用集合框架要健壮得多。
回答by gdejohn
Here's how to use generics to get an array of precisely the type you're looking for while preserving type safety (as opposed to the other answers, which will either give you back an Object
array or result in warnings at compile time):
以下是如何使用泛型在保留类型安全性的同时获取您正在寻找的类型的数组(与其他答案相反,后者将返回一个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];
}
}
That compiles without warnings, and as you can see in main
, for whatever type you declare an instance of GenSet
as, you can assign a
to an array of that type, and you can assign an element from a
to a variable of that type, meaning that the array and the values in the array are of the correct type.
编译时没有警告,正如您在 中看到的main
,对于您声明GenSet
as实例的任何类型,您都可以分配a
给该类型的数组,并且您可以将一个元素分配给a
该类型的变量,这意味着该数组并且数组中的值是正确的类型。
It works by using class literals as runtime type tokens, as discussed in the Java Tutorials. Class literals are treated by the compiler as instances of java.lang.Class
. To use one, simply follow the name of a class with .class
. So, String.class
acts as a Class
object representing the class String
. This also works for interfaces, enums, any-dimensional arrays (e.g. String[].class
), primitives (e.g. int.class
), and the keyword void
(i.e. void.class
).
它通过使用类文字作为运行时类型标记来工作,如Java 教程中所述。类文字被编译器视为java.lang.Class
. 要使用一个,只需在类名后面加上.class
. 因此,String.class
充当Class
表示类的对象String
。这也适用于接口、枚举、任意维数组(例如String[].class
)、原语(例如int.class
)和关键字void
(即void.class
)。
Class
itself is generic (declared as Class<T>
, where T
stands for the type that the Class
object is representing), meaning that the type of String.class
is Class<String>
.
Class
本身是泛型的(声明为Class<T>
,其中T
代表Class
对象所代表的类型),这意味着 的类型String.class
是Class<String>
。
So, whenever you call the constructor for GenSet
, you pass in a class literal for the first argument representing an array of the GenSet
instance's declared type (e.g. String[].class
for GenSet<String>
). Note that you won't be able to get an array of primitives, since primitives can't be used for type variables.
因此,每当您调用 for 的构造函数时GenSet
,您都会为表示GenSet
实例声明类型数组的第一个参数传入一个类文字(例如String[].class
for GenSet<String>
)。请注意,您将无法获得原语数组,因为原语不能用于类型变量。
Inside the constructor, calling the method cast
returns the passed Object
argument cast to the class represented by the Class
object on which the method was called. Calling the static method newInstance
in java.lang.reflect.Array
returns as an Object
an array of the type represented by the Class
object passed as the first argument and of the length specified by the int
passed as the second argument. Calling the method getComponentType
returns a Class
object representing the component type of the array represented by the Class
object on which the method was called (e.g. String.class
for String[].class
, null
if the Class
object doesn't represent an array).
在构造函数内部,调用该方法cast
会将传递的Object
参数强制转换为调用该方法的Class
对象所表示的类。调用静态方法newInstance
在java.lang.reflect.Array
返回作为Object
由所表示的类型的数组Class
作为第一个参数,并通过指定的长度的传递的对象int
通过作为第二个参数。调用该方法会getComponentType
返回一个Class
对象,该Class
对象表示由调用该方法的对象所表示的数组的组件类型(例如String.class
for String[].class
,null
如果该Class
对象不表示数组)。
That last sentence isn't entirely accurate. Calling String[].class.getComponentType()
returns a Class
object representing the class String
, but its type is Class<?>
, not Class<String>
, which is why you can't do something like the following.
最后一句话并不完全准确。调用String[].class.getComponentType()
返回一个Class
表示 class的对象String
,但它的类型是Class<?>
,不是Class<String>
,这就是您不能执行以下操作的原因。
String foo = String[].class.getComponentType().cast("bar"); // won't compile
Same goes for every method in Class
that returns a Class
object.
Class
返回Class
对象的每个方法也是如此。
Regarding Joachim Sauer's comment on this answer(I don't have enough reputation to comment on it myself), the example using the cast to T[]
will result in a warning because the compiler can't guarantee type safety in that case.
关于 Joachim Sauer 对此答案的评论(我自己没有足够的声誉来评论它),使用强制转换的示例T[]
将导致警告,因为在这种情况下编译器无法保证类型安全。
Edit regarding Ingo's comments:
编辑关于 Ingo 的评论:
public static <T> T[] newArray(Class<T[]> type, int size) {
return type.cast(Array.newInstance(type.getComponentType(), size));
}
回答by David Bernard
try this.
尝试这个。
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>();
}
}
}
回答by puneeth
Hi although the thread is dead, I would like to draw your attention to this:
嗨,虽然线程已死,但我想提请您注意这一点:
Generics is used for type checking during compile time:
泛型用于编译时的类型检查:
- Therefore the purpose is to check that what comes in is what you need.
- What you return is what the consumer needs.
- Check this:
- 因此,目的是检查进来的东西是否是您需要的。
- 您返回的就是消费者所需要的。
- 检查这个:
Do don't worry about typecasting warnings when you are writing generic class. Worry when you are using it.
在编写泛型类时不要担心类型转换警告。当你使用它时要担心。
回答by irreputable
This is the only answer that is type safe
这是唯一类型安全的答案
E[] a;
a = newArray(size);
@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
return Arrays.copyOf(array, length);
}