如何在Java中创建通用数组?
由于Java泛型的实现,您不能有这样的代码:如何在Java中创建通用数组?,java,arrays,generics,reflection,instantiation,Java,Arrays,Generics,Reflection,Instantiation,由于Java泛型的实现,您不能有这样的代码: public class GenSet<E> { private E a[]; public GenSet() { a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation } } 公共级发电机组{ 私人E a【】; 公共发电机组(){ a=新的E[初始数组长度];//错误:一般数组创建 } } 如何在维护类型安全的同时
public class GenSet<E> {
private E a[];
public GenSet() {
a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
}
}
公共级发电机组{
私人E a【】;
公共发电机组(){
a=新的E[初始数组长度];//错误:一般数组创建
}
}
如何在维护类型安全的同时实现此功能
我在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;
}
import java.lang.reflect.Array;
类堆栈{
公共堆栈(类clazz,int容量){
array=(T[])array.newInstance(clazz,capacity);
}
私有final T[]数组;
}
但是我真的不明白发生了什么。这个例子是使用Java反射创建一个数组。通常不建议这样做,因为它不是类型安全的。相反,您应该做的只是使用一个内部列表,并且完全避免使用数组。您可以创建一个对象数组,并将其强制转换为E everywhere。是的,这不是一种非常干净的方法,但至少应该有效。第25项的第5章(泛型)介绍了这一点。首选列表而非数组。 您的代码将正常工作,尽管它将生成未经检查的警告(您可以使用以下注释抑制该警告):
@SuppressWarnings({"unchecked"})
但是,最好使用列表而不是数组
有一个关于这个错误/特性的有趣的讨论。我必须问一个问题作为回报:您的
发电机组是“选中”还是“未选中”?
这是什么意思
- 选中:strong键入。
GenSet
明确知道它包含什么类型的对象(即,它的构造函数是用类
参数显式调用的,当传递的参数不是e
类型时,方法将引发异常。请参阅
->在这种情况下,你应该写:
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];
}
}
公共级发电机组{
私人E[]a;
公用发电机组(c类,int s){
//使用数组本机方法创建数组
//仅在运行时已知的类型
@抑制警告(“未选中”)
最终的E[]a=(E[])数组;
这个a=a;
}
E get(int i){
返回一个[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;
}
}
公共级发电机组{
私有对象[]a;
公共发电机组(int s){
a=新对象;
}
E get(int i){
@抑制警告(“未选中”)
最终E=(E)a[i];
返回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];
}
...
}
公共类发电机组{//E有一个Foo的上界
私有的Foo[]a;//E擦除为Foo,所以使用Foo[]
公共发电机组(int s){
a=新的Foo[s];
}
...
}
所有这些都源于Java中泛型的一个已知的、故意的弱点:它是使用擦除实现的,因此“泛型”类不知道它们在运行时使用的是什么类型参数,因此除非有明确的机制(类型检查),否则无法提供类型安全性Java泛型通过在编译时检查类型并插入适当的强制转换来工作,但会删除编译文件中的类型。这使得不理解泛型的代码可以使用泛型库(这是一个深思熟虑的设计决策)但这意味着您通常无法在运行时找到类型
公共堆栈(类clazz,int容量)
构造函数要求您在运行时传递类对象,这意味着类信息在运行时可用于需要它的代码。而类
表单意味着编译器将检查您传递的类对象是否正是类型T的类对象。不是T的子类,也不是T的超类,而恰恰是T。
这意味着您可以在构造函数中创建适当类型的数组对象,这意味着您存储在集合中的对象类型将在添加到集合时检查其类型。您可以执行以下操作:
E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];
这是在有效的Java中实现泛型集合的建议方法之一;第26项。无类型错误,无需重复强制转换数组。但是,这会触发警告,因为它具有潜在危险,应谨慎使用。如注释中所述,此对象[]
现在伪装成我们的E[]
类型,如果使用不安全,可能会导致意外错误或ClassCastException
s
根据经验,只要强制转换数组在内部使用(例如,用于支持数据结构),并且不返回或暴露给客户端代码,这种行为是安全的。如果需要将泛型类型的数组返回给其他代码,您提到的反射数组
类是正确的方法
值得一提的是,如果您使用泛型,在任何可能的情况下,使用List
s都会比使用数组快乐得多。当然,有时您没有选择余地,但使用集合框架要强大得多。下面介绍如何使用泛型来获得与您要查找的类型完全相同的数组ile保留类型安全性(与其他答案相反,其他答案要么返回对象
数组,要么在编译时产生警告):
Class
中返回Class
对象的每个方法也是如此
关于Joachim Sauer的评论(我自己没有足够的声誉对此发表评论),使用cast tot[]
的示例将导致警告,因为在这种情况下编译器无法保证类型安全
编辑关于国际非政府组织的评论:
public static <T> T[] newArray(Class<T[]> type, int size) {
return type.cast(Array.newInstance(type.getComponentType(), size));
}
publicstatict[]newArray(类类型,int-size){
返回type.cast(Array.newInstance(type.g
String foo = String[].class.getComponentType().cast("bar"); // won't compile
public static <T> T[] newArray(Class<T[]> type, int size) {
return type.cast(Array.newInstance(type.getComponentType(), size));
}
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>();
}
}
}
E[] a;
a = newArray(size);
@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
return Arrays.copyOf(array, length);
}
public class Whatever<Thing>{
private class Holder<OtherThing>{
OtherThing thing;
}
public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}
Object attributeValue = null;
try {
if(clazz.isArray()){
Class<?> arrayType = clazz.getComponentType();
attributeValue = Array.newInstance(arrayType, 0);
}
else if(!clazz.isInterface()){
attributeValue = BeanUtils.instantiateClass(clazz);
}
} catch (Exception e) {
logger.debug("Cannot instanciate \"{}\"", new Object[]{clazz});
}
if(clazz.isArray()){
Class<?> arrayType = clazz.getComponentType();
attributeValue = Array.newInstance(arrayType, 0);
}
public static <T> T[] toArray(final List<T> obj) {
if (obj == null || obj.isEmpty()) {
return null;
}
final T t = obj.get(0);
final T[] res = (T[]) Array.newInstance(t.getClass(), obj.size());
for (int i = 0; i < obj.size(); i++) {
res[i] = obj.get(i);
}
return res;
}
T[] array = (T[])Array.newInstance(cls, d1);
T[][] array = (T[][])Array.newInstance(cls, d1, d2);
T[][][] array = (T[][][])Array.newInstance(cls, d1, d2, d3);
T[][][][] array = (T[][][][])Array.newInstance(cls, d1, d2, d3, d4);
T[][][][][] array = (T[][][][][])Array.newInstance(cls, d1, d2, d3, d4, d5);
Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];
Tuple<Long, String>[] tupleArray = new Tuple[10];
Item<K>[] array = new Item[SIZE];
private K value;
@FunctionalInterface
interface ArraySupplier<E> {
E[] get(int length);
}
class GenericSet<E> {
private final ArraySupplier<E> supplier;
private E[] array;
GenericSet(ArraySupplier<E> supplier) {
this.supplier = supplier;
this.array = supplier.get(10);
}
public static void main(String[] args) {
GenericSet<String> ofString =
new GenericSet<>(String[]::new);
GenericSet<Double> ofDouble =
new GenericSet<>(Double[]::new);
}
}
public T [] createArray(int desiredSize){
ArrayList<T> builder = new ArrayList<T>();
for(int x=0;x<desiredSize;x++){
builder.add(null);
}
return builder.toArray(zeroArray());
}
//zeroArray should, in theory, create a zero-sized array of T
//when it is not given any parameters.
private T [] zeroArray(T... i){
return i;
}
public class GenSet<Item> {
private Item[] a;
public GenSet(int s) {
a = (Item[]) new Object[s];
}
}
private E a[];
private int size;
public GenSet(int elem)
{
size = elem;
a = (E[]) new E[size];
}
T[] array = (T[])new Object[SIZE];
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;
}
Stack<foo> = new Stack<foo>(foo.class,50)
Array.newInstance(clazz, capacity);
@SafeVarargs
public static <T> T[] toGenericArray(T ... elems) {
return elems;
}
List<Person>[] personLists=new ArrayList<Person>()[10];
import java.util.ArrayList;
import java.util.List;
public class PersonList {
List<Person> people;
public PersonList()
{
people=new ArrayList<Person>();
}
}
PersonList[] personLists=new PersonList[10];
class GenArray <T> {
private T theArray[]; // reference array
// ...
GenArray(T[] arr) {
theArray = arr;
}
// Do whatever with the array...
}
class GenArrayDemo {
public static void main(String[] args) {
int size = 10; // array size
// Here we can instantiate the array of the type we want, say Character (no primitive types allowed in generics)
Character[] ar = new Character[size];
GenArray<Character> = new Character<>(ar); // create the generic Array
// ...
}
}
class GenericInvoker <T> {
T variable;
public GenericInvoker(T variable){
this.variable = variable;
}
}
GenericInvoker<T>[] array;
public MyArray(){
array = new GenericInvoker[];
}
public T get(int index){
return array[index].variable;
}
public void resize(int newSize){
array = Arrays.copyOf(array, newSize);
}
public boolean add(T element){
// the variable size below is equal to how many times the add function has been called
// and is used to keep track of where to put the next variable in the array
arrays[size] = new GenericInvoker(element);
size++;
}
public class GenSet<T> {
private final T[] array;
@SafeVarargs
public GenSet(int capacity, T... dummy) {
if (dummy.length > 0)
throw new IllegalArgumentException(
"Do not provide values for dummy argument.");
this.array = Arrays.copyOf(dummy, capacity);
}
@Override
public String toString() {
return "GenSet of " + array.getClass().getComponentType().getName()
+ "[" + array.length + "]";
}
}
GenSet<Integer> intSet = new GenSet<>(3);
System.out.println(intSet);
System.out.println(new GenSet<String>(2));
GenSet of java.lang.Integer[3]
GenSet of java.lang.String[2]
public <T> T[] array(T... values) {
return values;
}
class Stack<T> {
private final T[] array;
public Stack(int capacity) {
array = (T[]) new Object[capacity];
}
}
GenSet<Integer> intSet[] = new GenSet[3];
for (int i = 0; i < 3; i++)
{
intSet[i] = new GenSet<Integer>();
}