34

If I have

public <T> doSomething(T[] array)
{
}

how can I get T.class from array?

If I do array.getClass() that gets me T[].class instead.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Jason S
  • 184,598
  • 164
  • 608
  • 970
  • 1
    (Note, [even if non-`null`] the array could be `String[]` giving `String.class`, but `T` could be `CharSequence`. Both reference arrays and reflection is best avoided. – Tom Hawtin - tackline Jan 19 '11 at 17:02
  • "reflection is best avoided": sure, but in my case T is an Enum, and I'm taking in a T[], doing some calculation and returning an EnumMap<> which needs a Class as a constructor parameter. – Jason S Jan 19 '11 at 18:58

3 Answers3

52

Component Type

Use this:

array.getClass().getComponentType()

Returns the Class representing the component type of an array. If this class does not represent an array class this method returns null.

Reference:


Safe / Unsafe casting

Is there a way I can cast to Class from Class returned by getComponentType() without getting a compiler warning?

take this method:

public <T> void doSomething(final T[] array) throws Exception{
    final Class<? extends Object[]> arrayClass = array.getClass();
    final Class<?> componentType = arrayClass.getComponentType();
    final T newInstance = (T) componentType.newInstance();
}

Here's the generated byte code:

public void doSomething(java.lang.Object[] array) throws java.lang.Exception;
     0  aload_1 [array]
     1  invokevirtual java.lang.Object.getClass() : java.lang.Class [21]
     4  astore_2 [arrayClass]
     5  aload_2 [arrayClass]
     6  invokevirtual java.lang.Class.getComponentType() : java.lang.Class [25]
     9  astore_3 [componentType]
    10  aload_3 [componentType]
    11  invokevirtual java.lang.Class.newInstance() : java.lang.Object [30]
    14  astore 4 [newInstance]
    16  return

As you can see, the parameter type is erased to Object[], so the compiler has no way to know what T is. Yes, the compiler could use array.getClass().getComponentType(), but that would sometimes fail miserably because you can do stuff like this:

Object[] arr = new String[] { "a", "b", "c" };
Integer[] integerArray = (Integer[]) arr;
doSomething(integerArray);

(In this case array.getClass().getComponentType() returns String.class, but T stands for Integer. Yes, this is legal and does not generate compiler warnings.)

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • cool, thanks. Is there a way I can cast to Class from Class> returned by getComponentType() without getting a compiler warning? – Jason S Jan 19 '11 at 16:12
  • 1
    @Jason S: I don't think there is a way to do that, because the compiler won't know what to cast to. – Sean Patrick Floyd Jan 19 '11 at 16:15
  • yes, but `Class` is just `Class` after type erasure, I guess. – Sean Patrick Floyd Jan 19 '11 at 16:22
  • sure, I understand the runtime issue, but the whole point of generics is that the compiler knows at compile time what types things are before the erasure step. – Jason S Jan 19 '11 at 16:42
  • ahhhhhh... cool, thanks for the detailed discussion and the counterexample. I forgot arrays are covariant (? did I get the vocab right) so that a String[] is also an Object[]. lots of +1's.... – Jason S Jan 19 '11 at 17:35
  • can you get array length somehow? if you do not have access to the original object? – voho Jan 23 '15 at 13:10
  • @voho unfortunately not without knowing whether it's a primitive or object array. Because you need to cast it to the array type and do arr.length on that – Sean Patrick Floyd Jan 23 '15 at 14:39
3

If you want to do this for multi-dimensional arrays the following recursive code will work

public static Class<?> getArrayType(Object array) {
    Object element = Array.get(array, 0);
    if (element.getClass().isArray()) {
        return getArrayType(element);
    } else {
        return array.getClass().getComponentType();
    }
}
James Mudd
  • 1,816
  • 1
  • 20
  • 25
0

Maybe an old thread, but I would like to share a tested version I did with all your recomendations:

    @NotNull
    @SafeVarargs
    public static <T> T coalesce(@NotNull T ... args) {
        for(T i : args) {
            if(i != null) {
                return i;
            }
        }
        T newInstance = null;
        try {
            final Class<? extends Object[]> arrayClass = args.getClass();
            final Class<?> componentType = arrayClass.getComponentType();
            newInstance = (T) componentType.newInstance();
        }catch(Exception e) {
            e.printStackTrace();
        }
        return newInstance;
    }

Hope will be usefull to you!

Best regards!

  • Now try writing a method ` T get() { return coalesce(); }` and calling it like `String s = get();`. [Oops.](https://ideone.com/8Im2NB) – shmosel Feb 02 '23 at 00:15