Creating New Array with Class Object in GWT

2.3k views Asked by At

I would like to create a new array with a given type from a class object in GWT.

What I mean is I would like to emulate the functionality of

java.lang.reflect.Array.newInstance(Class<?> componentClass, int size)

The reason I need this to occur is that I have a library which occasionally needs to do the following:

Class<?> cls = array.getClass();
Class<?> cmp = cls.getComponentType();

This works if I pass it an array class normally, but I can't dynamically create a new array from some arbitrary component type.

I am well aware of GWT's lack of reflection; I understand this. However, this seems feasible even given GWT's limited reflection. The reason I believe this is that in the implementation, there exists an inaccessible static method for creating a class object for an array.

Similarly, I understand the array methods to just be type-safe wrappers around JavaScript arrays, and so should be easily hackable, even if JSNI is required.

In reality, the more important thing would be getting the class object, I can work around not being able to make new arrays.

4

There are 4 answers

0
Andrew Boardman On BEST ANSWER

The way that I did a similar thing was to pass an empty, 0 length array to the constructor of the object that will want to create the array from.

public class Foo extends Bar<Baz> {

    public Foo()
    {
        super(new Baz[0]);
    }
...
}

Baz:

public abstract class Baz<T>
{
    private T[] emptyArray;

    public Baz(T[] emptyArray)
    {
        this.emptyArray = emptyArray;
    }
...
}

In this case the Bar class can't directly create new T[10], but we can do this:

ArrayList<T> al = new ArrayList<T>();

// add the items you want etc

T[] theArray = al.toArray(emptyArray);

And you get your array in a typesafe way (otherwise in your call super(new Baz[0]); will cause a compiler error).

0
demawi On

For implementing an array concatenation method, I also stepped into the issue of missing Array.newInstance-method.

It's still not implemented, but if you have an existing array you can use

Arrays.copyOf(T[] original, int newLength)

instead.

0
Ajax On

If you are cool with creating a seed array of the correct type, you can use jsni along with some knowledge of super-super-source to create arrays WITHOUT copying through ArrayList (I avoid java.util overhead like the plague):

public static native <T> T[] newArray(T[] seed, int length)
/*-{
return @com.google.gwt.lang.Array::createFrom([Ljava/lang/Object;I)(seed, length);
}-*/;

Where seed is a zero-length array of the correct type you want, and length is the length you want (although, in production mode, arrays don't really have upper bounds, it makes the [].length field work correctly).

The com.google.gwt.lang package is a set of core utilities used in the compiler for base emulation, and can be found in gwt-dev!com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang.

You can only use these classes through jsni calls, and only in production gwt code (use if GWT.isProdMode()). In general, if you only access the com.google.gwt.lang classes in super-source code, you are guaranteed to never leak references to classes that only exist in compiled javascript.

if (GWT.isProdMode()){
  return newArray(seed, length);
}else{
  return Array.newInstance(seed.getComponentType(), length);
}

Note, you'll probably need to super-source the java.lang.reflect.Array class to avoid gwt compiler error, which suggests you'll want to put your native helper method there. However, I can't help you more than this, as it would overstep the bounds of my work contract.

0
user3864189 On

I had to do something similar, I found it was possible using the Guava library's ObjectArrays class. Instead of the class object it requires a reference to an existing array.

T[] newArray = ObjectArrays.newArray(oldArray, oldArray.length);