将光标移到/点击文章中的句子上,可以查看译文。      显示繁体中文内容    显示简体中文内容

How to create a generic array in Java?
java如何创建泛型数组?

Due to the implementation of Java generics, you can't have code like this :


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 :


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.can anyone help?

时间:

I have to ask a question in return :is your GenSet"checked"or"unchecked"?what does that mean?

  • Checked : strong typing.GenSet knows explicitly what type of objects it contains (i.e.its constructor was explicitly called with a Class<E> argument, and methods will throw an exception when they are passed arguments that are not of type E.see Collections.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 erasure of 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];
     }
    
    . . .
    }
    
    

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.

A quick test confirms you can do this also :


E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

My test :


public class ArrTest<E> {
 public static void main(String[] args){
 ArrTest<String> t = new ArrTest<String>();
 t.test("Hello World");
 }

 public void test(E a){
 E[] b = (E[])new Object[1];
 b[0] = a;
 System.out.println(b[0]);
 }
}

No warnings, no type errors, no need to cast the array repeatedly.however this 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.

As a rule of thumb, this behavior is safe as long as the cast array is used internally, 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.


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.

To extend to more dimensions, just add [] 's and dimension parameters to newInstance() ( T is a type parameter, cls is a Class<T>, d1 through d5 are integers) :


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);

See Array.newInstance() for details.

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);
}

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 :

enter image description here

Do don't worry about typecasting warnings when you are writing generic class.Worry when you are using it.

This is covered in Chapter 5 (Generics) of Effective Java, 2 nd Edition, item 25... Prefer lists to arrays

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.

There's an interesting discussion of this bug/feature on Sun's site.

Java generics work by checking types at compile time and inserting appropriate casts, but erasing the 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.

The publicStack(Class<T> clazz,int capacity)constructor requires you to pass a Class object at run time, which means class information is available 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.

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.

...