Creating New Array with Class Object in GWT

2019-06-24 14:04发布

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.

标签: arrays gwt jsni
3条回答
欢心
2楼-- · 2019-06-24 14:42

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.

查看更多
疯言疯语
3楼-- · 2019-06-24 14:43

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

查看更多
做自己的国王
4楼-- · 2019-06-24 14:52

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);
查看更多
登录 后发表回答