35

Here is a variable Class<?> cls, now I want to get another Array Class Object which component type is cls.

For example, if cls=String.class , I want to get String[].class; if cls=int.class, I want to get int[].class, what should I do?

You see, It's quite easy to get String.class from String[].class:

Class<?> arrayCls = String[].class;
if(arrayCls.isArray()){
    Class<?> cls = arrayCls.getComponentType();
}

But I cannot find easy way to do the reverse.

Here is one possible solution:

Class<?> clazz = String.class;
Class<?> arrayClass = Array.newInstance(clazz,0).getClass();

Is there any batter way to do this please?

Jin Kwon
  • 20,295
  • 14
  • 115
  • 184
watchzerg
  • 652
  • 1
  • 7
  • 12
  • 1
    What's wrong with that solution? – Thilo Nov 15 '12 at 05:47
  • @Thilo The solution is a result of my guess. It works fine, but I'm not sure if this is the best way. This solution need to dynamic create an array entity which length is 0. – watchzerg Nov 15 '12 at 05:52
  • Interesting question. Just curious why you need the array `Class` object? – Paul Bellora Nov 15 '12 at 06:32
  • `Array.newInstance(clazz,0)` creates a short-lived object on the operand stack, the JIT might optimize that away but I wouldn't count on it. – Coder Guy Nov 05 '19 at 01:37

3 Answers3

18

HRgiger's answer improved:

@SuppressWarnings("unchecked")
static <T> Class<? extends T[]> getArrayClass(Class<T> clazz) {
    return (Class<? extends T[]>) Array.newInstance(clazz, 0).getClass();
}

Both of them instantiate an array object when invoked. To get the array type, use

Class<?> childType = ...;
Class<?> arrayType = getArrayClass(childType);
Limeth
  • 513
  • 6
  • 16
  • 1
    This has nothing to do with HRgiger’s answer, this is an improvement of what the OP posted in the question. – Holger Jul 07 '17 at 16:09
  • Also, I'm pretty sure you're instantiating a new object here, every time, which the other answer isn't. – Sander Verhagen Jan 04 '18 at 04:38
  • 1
    Beware that this unchecked cast is broken if you pass it a primitive type. For example, `int.class` is a `Class`, but `int[].class` is a `Class`, not a `Class`. The method should just return a `Class>`, or it should check if `clazz.isPrimitive()` and throw an exception if it is. – Radiodef Mar 22 '18 at 17:19
  • Why ` extends T[]>`? I understand why `getClass()` returns that in general, but in this case, we know that the array has the exact type `T[]` (assuming clazz isn't primitive, but that breaks this anyway), so as long as we're doing an unchecked cast anyway, why not be more precise? – Luke Maurer Dec 24 '19 at 00:21
10

Maybe try Class.forName(String)?

Edit: Here is a code snippet.

Class<?> arrayClass = String[].class;
System.out.println(arrayClass);
Class<?> namedClass = Class.forName("[L" + String.class.getName() + ";");
System.out.println(namedClass);
System.out.println(arrayClass == namedClass);
zienkikk
  • 2,404
  • 1
  • 21
  • 28
  • 1
    According XMLEncoder Class in jdk, `Class.forName(String)` is the standard method to do this. Thanks dude. `Class> cls=String.class; Class> arrayCls=Class.forName("[L"+cls.getName()+";");` http://stackoverflow.com/questions/4581394/getting-the-class-of-an-n-dimensional-array-of-an-runtime-supplied-class-name – watchzerg Nov 15 '12 at 06:20
  • Ah. I forgot about the semicolon at the end. – zienkikk Nov 15 '12 at 06:25
  • 3
    Personally I prefer your solution using `Array.newInstance`. This at least uses documented features. But the name prefix `[L` is an internal convention. In the future they could change it and change the `XMLEncoder` class, because that's part of the JDK also. Just because something does something in a certain way in the JDK, doesn't mean that's the way you should do it, because you are writing application code rather than JDK code. – Adam Burley Sep 28 '15 at 21:57
  • 10
    @Kidburla: The naming [is more than a convention](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getName--). Still, creating a single zero sized array is way cheaper than the string concatenation, not to speak of the dynamic class lookup performed in `Class.forName` afterwards. And if you have an arbitrary `Class` object, `Class.forName("[L"+clazz.getName()+";");` will fail if `clazz` represents a primitive type or an array type, so the bottom line is, `Array.newInstance(clazz,0).getClass()` is *way better* – Holger Jul 07 '17 at 13:43
  • @Holger Should write an answer. You are 100000% correct. OP even mentioned the requirement of primitives & dynamic nature requirement (latter is mentioned in comments). It's as if the OP isn't testing their code against the requirements they specified. I know this question is old, but you'd probably pass the current accepted answer within time, and it's more introspective of the situation at hand. – Vince Sep 30 '17 at 07:35
10

Another trick I found is using varargs on util method.

public static void main(String[] args) throws ClassNotFoundException {

    Class<?> demo = Main.<String>getArrayClass();
    System.out.println(demo);
}

static <T> Class getArrayClass(T... param){
    return param.getClass();
}
HRgiger
  • 2,750
  • 26
  • 37
  • 1
    Nice trick +1 Actually if you specify `T` at the call site there's no need to pass any arguments: `MyClass.getArrayClass();` – Paul Bellora Nov 15 '12 at 06:48
  • yes good tip:) I have updated! – HRgiger Nov 15 '12 at 07:09
  • 5
    Why won't this return the class of `Object[]` everytime because of runtime type erasure? – SpaceTrucker Nov 15 '12 at 08:05
  • 9
    Very nice trick. It does not help the OP though, because you need to know the class in compile time. And if you know at compile time that it's `String`, you can just say `String[].class`. – Saintali Nov 15 '12 at 08:42
  • 1
    @SpaceTrucker, Saintali right, I didnt spent much time, because there is already a solution, just wanted to share maybe for similar cases can be good option:) – HRgiger Nov 15 '12 at 14:07
  • 2
    @SpaceTrucker, this is a late response but after looking into this it appears that type erasure doesn't apply here because the varargs argument type is known at compile time for the method call. If we didn't know the argument type (e.g., ` void otherMethod(U obj) { System.out.println(getArrayClass(obj)); }`) then type erasure would apply and `T... param` would be always be an `Object[]`, in which case `Object[].class` would always be returned. – Mike Hill Feb 16 '17 at 15:14