Java 泛型列表数组
我在玩泛型和数组,似乎下面的代码编译得很好Java 泛型列表数组,java,generics,Java,Generics,我在玩泛型和数组,似乎下面的代码编译得很好 ArrayList<Key> a = new ArrayList<Key>(); ArrayList a=新的ArrayList(); 但是编译器抱怨这一点 ArrayList<Key>[] a = new ArrayList<Key>[10]; ArrayList[]a=新的ArrayList[10]; 通过阅读stackoverflow中的post,我了解到这是由于类型擦除造成的,我可以使用
ArrayList<Key> a = new ArrayList<Key>();
ArrayList a=新的ArrayList();
但是编译器抱怨这一点
ArrayList<Key>[] a = new ArrayList<Key>[10];
ArrayList[]a=新的ArrayList[10];
通过阅读stackoverflow中的post,我了解到这是由于类型擦除造成的,我可以使用
ArrayList<Key>[] a = (ArrayList<Key> []) new ArrayList[10];
ArrayList[]a=(ArrayList[])新的ArrayList[10];
或列表
ArrayList<ArrayList<Key>> b = new ArrayList<ArrayList<Key>>();
ArrayList b=新的ArrayList();
但我想不出幕后的原因。特别是,考虑到第一个问题,为什么第二个问题是非法的是完全可以的。以及为什么编译器不抱怨列表的列表。您不能拥有数组,因为数组需要原始类型。您可以在第二个实例中对其进行类型转换,这使其符合定义的类型,因此是合法的(但是,这是无法推断的)。列表列表是合法的,因为
ArrayList
不是数组
请参阅中的第7.3章(第15页),以了解有关这方面的更多详细信息
数组对象的组件类型不能是类型变量或
参数化类型,除非它是(无界)通配符类型。您可以
声明其元素类型为类型变量或
参数化类型,但不是数组对象。
当然,这很烦人。此限制对于避免以下情况是必要的:
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[]lsa=新列表[10];//不允许
对象o=lsa;
对象[]oa=(对象[])o;
List li=new ArrayList();
加(新整数(3));
oa[1]=li;//不可靠,但通过运行时存储检查
字符串s=lsa[1]。获取(0);//运行时错误-ClassCastException
如果允许参数化类型的数组,则上面的示例将
编译时没有任何未检查的警告,但在运行时失败
本教程接着介绍以下内容:
由于类型变量在运行时不存在,因此无法确定
实际数组类型为。
解决这些限制的方法是使用类文本作为运行时
类型标记
数组允许逃避类型检查(如Chris的回答所示)。所以,您可以有一个通过所有编译器检查的代码(没有来自编译器的“未检查”警告),但在运行时由于ClassCastException而失败。 禁止这种构造会给开发人员带来问题,因此警告确实会出现。我有一个自己的FWIW,我觉得答案没有说服力。最详细答案的相关部分(参考pdf参考)如下: 数组对象的组件类型不能是类型变量或 参数化类型,除非它是(无界)通配符类型。您可以 声明其元素类型为类型变量或 参数化类型,但不是数组对象。老实说,这很烦人 当然此限制对于避免以下情况是必要的:
List[]lsa=新列表[10];//不允许
对象o=lsa;
对象[]oa=(对象[])o;
List li=new ArrayList();
加(新整数(3));
oa[1]=li;//不可靠,但通过运行时存储检查
字符串s=lsa[1]。获取(0);//运行时错误-ClassCastException
因此,因为我可以将列表[]cat到对象[],然后将不正确的内容推入对象[],然后从列表引用中通过casted ref错误引用,这是坏的/不允许的?但只有新的吗
对于我来说,用new来声明这一点与用法相比或多或少是一个问题,这一点仍然有点模糊,我仍然盯着它看,希望它开始有意义,或者至少能解析成一个漂亮的3d图像。创建通用数组不是类型安全的(请参阅约书亚·布洛赫的《有效的Java——第二版》 使用:
List b=新的数组列表(10);
或使用Java SE 7:
List<List<Key>> b = new ArrayList<>(10);
List b=新的数组列表(10);
数组是穷人的泛型;对于真正的泛型,人们应该避免使用数组,尽管并不总是可能的
数组是协变的,泛型是不变的;再加上擦除,情况就不太适合了,正如Chris回答中的示例所示
不过,我认为可以放宽规范,允许创建泛型数组-这真的没有问题。当抛出数组时,危险就来了;此时一个编译器警告就足够了
实际上Java确实为vararg方法创建了泛型数组,所以这有点虚伪
下面是利用这一事实的实用方法
@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);
@SafeVarargs
静态E[]阵列并行(E…阵列)
{
返回数组;
}
@安全变量
静态E[]新数组(整数长度,E…数组)
{
返回数组.copyOf(数组,长度);
}
//用法
列表[]数组1=数组并行(列表,列表);
列表[]数组2=newArray(10);
编译器需要保证泛型类型安全;如果不能,则必须禁用代码或发出警告。在本例中,第2行的警告似乎足够了;java完全禁用泛型数组创建的决定似乎过于苛刻。是ArrayList[]a=(ArrayList[])new ArrayList[10]
仍然存在同样的问题,但这更清楚地表明您在欺骗类型系统(并生成警告)。finnw,您的评论是我认为正确的答案,也是唯一对我有意义的方法。这可能是因为向后兼容。假设某个未来版本(Java 9?)允许创建通用数组,然后当Java 9代码与Java 5/6/7/8代码混合时(可以在没有警告的情况下向上转换数组),结果将是意外的ClassCastException。请注意,在这些代码示例中,创建的数组的实际类型不是E[]
,但删除某些推断类型的E
。例如,如果执行静态T[]m(){T[]a=newArray(0);返回a;}
,则为a的实际类型
List<List<Key>> b = new ArrayList<>(10);
@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);