我不理解泛型和数组之间的联系。
我可以用泛型类型创建数组引用:
1
| private E[] elements; //GOOD |
但无法创建具有泛型类型的数组对象:
1
| elements = new E[10]; //ERROR |
但它是有效的:
1
| elements = (E []) new Object[10]; //GOOD |
- 是的,使用new ArrayList()???????
您不应该混淆数组和泛型。他们相处得不好。数组和泛型类型执行类型检查的方式存在差异。我们说数组是真实化的,但泛型不是。因此,您可以看到使用数组和泛型时的这些差异。好的。数组是协变的,泛型不是:
这意味着什么?您现在必须知道以下分配有效:好的。
基本上,Object[]是String[]的超类型,因为Object是String的超类型。对于仿制药,这是不正确的。因此,以下声明无效,无法编译:好的。
1
| List<Object> list = new ArrayList<String>(); // Will not compile. |
原因是,泛型是不变的。好的。强制类型检查:
在Java中引入泛型,以在编译时执行更强的类型检查。因此,由于类型擦除,泛型类型在运行时没有任何类型信息。因此,List有一个静态类型的List,而动态类型的List。好的。
但是,数组携带组件类型的运行时类型信息。在运行时,数组使用数组存储检查来检查是否插入与实际数组类型兼容的元素。因此,以下代码:好的。
将编译良好,但在运行时由于arraystorecheck而失败。对于泛型,这是不可能的,因为编译器将尝试通过提供编译时检查来防止运行时异常,避免创建类似这样的引用,如上图所示。好的。那么,创建通用数组有什么问题?
创建组件类型为类型参数、具体参数化类型或有界通配符参数化类型的数组是不安全的类型。好的。
请考虑以下代码:好的。
1 2 3 4
| public <T> T[] getArray(int size) {
T[] arr = new T[size]; // Suppose this was allowed for the time being.
return arr;
} |
由于运行时不知道T的类型,因此创建的数组实际上是Object[]。因此,运行时的上述方法如下:好的。
现在,假设您将此方法称为:好的。
这就是问题所在。您刚刚将一个Object[]分配给一个Integer[]的引用。上面的代码编译得很好,但在运行时会失败。好的。
这就是禁止创建泛型数组的原因。好的。为什么把new Object[10]打字到E[]工作?
现在你最后一个疑问,为什么下面的代码有效:好的。
1
| E [] elements = (E []) new Object[10]; |
上面的代码与上面解释的含义相同。如果您注意到了,编译器将在这里向您发出未选中的强制转换警告,因为您正在将类型转换为未知组件类型的数组。这意味着,强制转换可能在运行时失败。例如,如果您在上述方法中有该代码:好的。
1 2 3 4
| public <T > T [] getArray (int size ) {
T [] arr = (T [])new Object[size ];
return arr ;
} |
你可以这样调用它:好的。
这将在运行时以ClassCastException失败。所以,不,这种方法不会一直有效。好的。创建一个类型为List[]的数组怎么样?
问题是一样的。由于类型擦除,List[]只不过是List[]而已。因此,如果允许创建这样的数组,让我们看看会发生什么:好的。
1 2 3
| List <String >[] strlistarr = new List <String >[10]; // Won't compile. but just consider it
Object[] objarr = strlistarr ; // this will be fine
objarr [0] = new ArrayList <Integer >(); // This should fail but succeeds. |
现在,上述情况下的arraystorecheck将在运行时成功,尽管这应该引发arraystoreException。这是因为List[]和List[]都是在运行时编译到List[]的。好的。那么我们可以创建一个无限通配符参数化类型的数组吗?
对。原因是,List>是一种可再流通的类型。这是有意义的,因为根本没有关联的类型。所以没有什么可以因为类型擦除而松脱的。因此,创建这种类型的数组是完全类型安全的。好的。
1 2 3
| List<?>[] listArr = new List<?>[10];
listArr[0] = new ArrayList<String>(); // Fine.
listArr[1] = new ArrayList<Integer>(); // Fine |
以上两种情况都可以,因为List>是泛型类型List的所有实例化的超级类型。因此,它不会在运行时发出arraystoreException。这种情况与原始类型数组相同。由于原始类型也是可重写的类型,因此可以创建数组List[]。好的。
所以,它就像,您只能创建一个可重设类型数组,而不能创建不可重设类型数组。注意,在上述所有情况下,数组的声明都是好的,它是使用new操作符创建的数组,这就产生了问题。但是,声明这些引用类型的数组是没有意义的,因为它们只能指向null以外的任何对象(忽略未绑定的类型)。好的。是否有针对E[]的解决方案?
是的,可以使用Array#newInstance()方法创建数组:好的。
1 2 3 4 5 6
| public <E > E [] getArray (Class <E > clazz, int size ) {
@SuppressWarnings ("unchecked")
E [] arr = (E []) Array. newInstance(clazz, size );
return arr ;
} |
类型转换是必需的,因为该方法返回一个Object。但你可以确定这是一个安全的演员。所以,您甚至可以在该变量上使用@suppresswarnings。好的。好啊。
- 太完美了,谢谢。:)
- nitpick:"创建组件类型为…通配符参数化类型是不安全的类型。"实际上,实例化(例如,new List>[] { }是有效的——只是通配符不能被绑定。
- 另外,"这将在运行时用ClassCastException失败。"不是完全正确的。当一个演员表未选中时,这意味着它不会很快失败。相反,ClassCastException可以在编译器在擦除过程中插入了Cast的其他地方抛出。这个问题就是一个很好的例子。
- @ PaulBellora。实际上,我的意思是有界。漏掉了这个词。将编辑谢谢:)
- @ PaulBellora。至于铸造部分,我写的是,为了铸造到江户十一〔八〕号,那肯定会失败的。编辑了那个部分以使其清晰。
- 真棒!+ 1
- @ PaulBellora。感谢您的反馈:)
- 哦,泛型在Java中实现得如此糟糕。
- "由于T的类型在运行时未知,因此创建的数组实际上是一个对象[]"。为什么在运行时不知道?即使在编译时也知道它,我怎么会错呢?
- @克劳斯,我不确定我有你的问题吗?您想知道为什么它在运行时是未知的,或者为什么它在编译时是已知的吗?
- 我的第一个问题是:我想知道为什么在运行时不知道它?
- @克劳斯因为类型擦除。stackoverflow.com/questions/339699/&hellip;
- @Rohitjain为什么新的t[]不能转换为给定的论点?像新的字符串[]或其他什么?不是Object[]?
- @Rohitjain我知道使用反射有一个性能惩罚。在这种情况下,如果是本机方法,我们能知道吗?我想要看情况,嗯?
问题是,当运行时泛型类型被删除时,new E[10]将等效于new Object[10]。
这是危险的,因为可以将除E类型以外的数据放入数组中。这就是为什么你需要明确地说出你想要的类型
- 创建对象数组并将其强制转换为E[]数组,或
- 使用array.newinstance(class componenttype,int length)创建在componentType参数中传递的类型的数组的实际实例。
- 但是(e[])不会通过类型擦除转换为(o[])?
- @用户2693979会的。泛型是编译器工具,而不是运行时。我不太明白你想表现什么问题…
- @用户2693979如果你认为更好,你应该接受Rohit的答案。没有压力,只是因为我早点贴了。
- 但如果e[]将是对象[],而(e[])将是(对象[]),那么为什么(e=新e[10])和(e=(e[])对象[10])之间存在差异?不是都是e=新对象[10]?
- @用户2693979我怀疑不允许new E[size]阻止我们认为我们实际上是在创建E类型的数组,而不是Object类型的数组。我在回答中提到的方式清楚地表明了正在发生的事情,而new E[size]可以被错误地解释。但同样,这只是我的怀疑。
- @用户2693979简单地说,new E[size]被编译器阻止,因为它不会创建E[]数组(仅E类型的数组)而创建Object[]数组。
下面是LinkedList#toArray(T[])的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public <T > T [] toArray (T [] a ) {
if (a. length < size )
a = (T [])java. lang. reflect. Array. newInstance(
a. getClass(). getComponentType(), size );
int i = 0;
Object[] result = a ;
for (Node <E > x = first ; x != null; x = x. next)
result [i ++] = x. item;
if (a. length > size )
a [size ] = null;
return a ;
} |
简而言之,您只能通过Array.newInstance(Class, int)创建通用数组,其中int是数组的大小。
选中的:
1 2 3 4
| public Constructor(Class <E > c, int length ) {
elements = (E []) Array. newInstance(c, length );
} |
或未经检查:
- 很抱歉,我忘记解释他应该像私有对象[]元素那样更改元素类型;而不是使用泛型类型。