Java 如何正确使用泛型类型的数组?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/897251/
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 do I use arrays of generic types correctly?
提问by Hanno Fietz
I have a class that maps incoming messages to matching readers based on the message's class. All message types implement the interface message. A reader registers at the mapper class, stating which message types it will be able to handle. This information needs to be stored in the message reader in some way and my approach was to set a private final
array from the constructor.
我有一个类,它根据消息的类将传入的消息映射到匹配的阅读器。所有消息类型都实现了接口消息。阅读器在映射器类中注册,说明它将能够处理哪些消息类型。这些信息需要以某种方式存储在消息阅读器中,我的方法是private final
从构造函数设置一个数组。
Now, it seems I have some misunderstanding about generics and / or arrays, that I can't seem to figure out, see the code below. What is it?
现在,我似乎对泛型和/或数组有一些误解,我似乎无法弄清楚,请参阅下面的代码。它是什么?
public class HttpGetMessageReader implements IMessageReader {
// gives a warning because the type parameter is missing
// also, I actually want to be more restrictive than that
//
// private final Class[] _rgAccepted;
// works here, but see below
private final Class<? extends IMessage>[] _rgAccepted;
public HttpGetMessageReader()
{
// works here, but see above
// this._rgAccepted = new Class[1];
// gives the error "Can't create a generic array of Class<? extends IMessage>"
this._rgAccepted = new Class<? extends IMessage>[1];
this._rgAccepted[0] = HttpGetMessage.class;
}
}
ETA:
As cletus correctly pointed out, the most basic googling shows that Java does not permit generic arrays. I definitely understand this for the examples given (like E[] arr = new E[8]
, where E
is a type parameter of the surrounding class). But why is new Class[n]
allowed? And what then is the "proper" (or at least, common) way to do this?
ETA:
正如 cletus 正确指出的那样,最基本的谷歌搜索表明 Java 不允许通用数组。对于给出的示例,我绝对理解这一点(例如E[] arr = new E[8]
, whereE
是周围类的类型参数)。但为什么是new Class[n]
允许的?那么这样做的“正确”(或至少是常见的)方法是什么?
采纳答案by cletus
Java does not permit generic arrays. More information in the Java Generics FAQ.
Java 不允许通用数组。Java 泛型常见问题解答中的更多信息。
To answer your question, just use a List(probably ArrayList) instead of an array.
要回答您的问题,只需使用List(可能是ArrayList)而不是数组。
Some more explanation can be found in Java theory and practice: Generics gotchas:
更多的解释可以在Java 理论和实践中找到:泛型陷阱:
Generics are not covariant
While you might find it helpful to think of collections as being an abstraction of arrays, they have some special properties that collections do not. Arrays in the Java language are covariant -- which means that if Integer extends Number (which it does), then not only is an
Integer
also a Number, but anInteger[]
is also aNumber[]
, and you are free to pass or assign anInteger[]
where aNumber[]
is called for. (More formally, ifNumber
is a supertype ofInteger
, thenNumber[]
is a supertype ofInteger[]
.) You might think the same is true of generic types as well -- thatList<Number>
is a supertype ofList<Integer>
, and that you can pass aList<Integer>
where aList<Number>
is expected. Unfortunately, it doesn't work that way.It turns out there's a good reason it doesn't work that way: It would break the type safety generics were supposed to provide. Imagine you could assign a
List<Integer>
to aList<Number>
. Then the following code would allow you to put something that wasn't anInteger
into aList<Integer>
:List<Integer> li = new ArrayList<Integer>(); List<Number> ln = li; // illegal ln.add(new Float(3.1415));
Because ln is a
List<Number>
, adding aFloat
to it seems perfectly legal. But if ln were aliased with li, then it would break the type-safety promise implicit in the definition of li -- that it is a list of integers, which is why generic types cannot be covariant.
泛型不是协变的
虽然您可能会发现将集合视为数组的抽象很有帮助,但它们具有集合所没有的一些特殊属性。Java 语言中的数组是协变的——这意味着如果 Integer 扩展了 Number(它确实如此),那么不仅 an
Integer
也是一个 Number,而且 anInteger[]
也是 aNumber[]
,并且您可以自由地传递或分配一个Integer[]
aNumber[]
被调用的地方为了。(更正式地说,如果Number
是 的超类型Integer
,则Number[]
是 的超类型Integer[]
。)您可能认为泛型类型也是如此——它List<Number>
是 的超类型List<Integer>
,并且您可以List<Integer>
在需要a 的地方传递 aList<Number>
。不幸的是,它不能那样工作。事实证明,它不能那样工作有一个很好的理由:它会破坏类型安全泛型应该提供的。想象一下,您可以将 a 分配
List<Integer>
给 aList<Number>
。然后以下代码将允许您将不是 an 的内容Integer
放入 aList<Integer>
:List<Integer> li = new ArrayList<Integer>(); List<Number> ln = li; // illegal ln.add(new Float(3.1415));
因为 ln 是 a
List<Number>
,向Float
它添加 a似乎完全合法。但是如果 ln 与 li 别名,那么它会破坏 li 定义中隐含的类型安全承诺——它是一个整数列表,这就是泛型类型不能协变的原因。
回答by Kris
Arrays are always of a specific type, unlike how Collections used to be before Generics.
数组始终是特定类型的,这与在泛型之前的集合不同。
Instead of
代替
Class<? extends IMessage>[] _rgAccepted;
You should simply write
你应该简单地写
IMessage[] _rgAccepted;
Generics don't enter into it.
泛型不进入它。
回答by dfa
And what then is the "proper" (or at least, common) way to do this?
那么这样做的“正确”(或至少是常见的)方法是什么?
@SuppressWarnings(value="unchecked")
public <T> T[] of(Class<T> componentType, int size) {
return (T[]) Array.newInstance(componentType, size);
}
public demo() {
Integer[] a = of(Integer.class, 10);
System.out.println(Arrays.toString(a));
}
回答by jrudolph
It is right what cletus said. There is a general mismatch between the usually enforced invariance of generic type parameters vs. covariance of Java arrays.
克莱图斯说的没错。通常强制的泛型类型参数不变性与 Java 数组的协变性之间存在普遍的不匹配。
(Some background: Variance specifies how types relate to each other concerning subtyping. I.e. because of generic type parameters being invariant Collection <: Collection does not hold. So, concerning the Java type system, a String collection is no CharSequence collection. Arrays being covariant means that for any types T and U with T<:U, T[] <: U[]. So, you can save a variable of type T[] into a variable of type U[]. Since there is a natural need for other forms of variance, Java at least allows wildcards for these purposes.)
(一些背景:Variance 指定了关于子类型的类型如何相互关联。即因为泛型类型参数是不变的 Collection <: Collection 不成立。因此,对于 Java 类型系统,String 集合不是 CharSequence 集合。数组是协变的意味着对于任何类型 T 和 U 与 T<:U, T[] <: U[]。因此,您可以将 T[] 类型的变量保存到 U[] 类型的变量中。由于有一个自然需要对于其他形式的差异,Java 至少允许用于这些目的的通配符。)
The solution (the hack, actually) I often use, is declaring a helper method which generates the array:
我经常使用的解决方案(实际上是 hack)是声明一个生成数组的辅助方法:
public static <T> T[] array(T...els){
return els;
}
void test(){
// warning here: Type safety : A generic array of Class is created for a varargs parameter
Class<? extends CharSequence>[] classes = array(String.class,CharSequence.class);
}
Because of erasure the generated arrays will always be of type Object[].
由于擦除,生成的数组将始终是 Object[] 类型。
回答by newacct
IMHO,
恕我直言,
this._rgAccepted = (Class<? extends IMessage>[])new Class[1];
isthe appropriate way to handle this. Array component types have to be reified types, and Class
is the closest reified type to Class<whatever>
. It'll work just as you would expect a Class<whatever>[]
to work.
是处理此问题的适当方法。数组组件类型必须是具体化类型,并且Class
是最接近的具体化类型Class<whatever>
。它会像您期望的那样Class<whatever>[]
工作。
Yes, technically it is unchecked and might cause issues later if you cast it to another more general array type and put wrong stuff in it, but since this is a private field in your class, I presume you can make sure it is used appropriately.
是的,从技术上讲,它是未经检查的,如果您将其转换为另一个更通用的数组类型并在其中放入错误的内容,可能会在以后导致问题,但由于这是您班级中的私有字段,我认为您可以确保它被正确使用。