I have a Class
variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this:
Class arrayOfFooClass = java.lang.reflect.Array.开发者_如何学JAVAnewInstance(fooClass, 0).getClass();
Is there a way to do this without creating the new instance?
Since Java 12
Class
provides a method arrayType()
, which returns the array type class whose component type is described by the given Class. Please be aware that the individual JDK may still create an instance of that Class³.
Class<?> stringArrayClass = FooBar.arrayType()
Before Java 12
If you don't want to create an instance, you could create the canonical name of the array manually and get the class by name:
// Replace `String` by your object type.
Class<?> stringArrayClass = Class.forName(
"[L" + String.class.getCanonicalName() + ";"
);
But Jakob Jenkov argues in his blog that your solution is the better one, because it doesn't need fiddling with strings.
Class<?> stringArrayClass = Array.newInstance(String.class, 0).getClass();
³ Thanks for the hint to Johannes Kuhn.
Since Java 12, there is the arrayType()
method on java.lang.Class. With that:
Class<?> arrayOfFooClass = fooClass.arrayType();
The implementation of Class.arrayType()
just calls Arrays.newInstance(this, 0).getClass()
.
You can get it using the class name. Just make sure you get the class using it's ClassLoader
Class klass = yourClass;
boolean init = wantToDoStaticOperations;
Class.forName("[L" + klass.getName() + ";", init, klass.getClassLoader());
So, I for one, like fiddling with Strings. So, here's a more general solution that takes that approach, and still works with arbitrary class types. It is certainly more fiddly than your answer, but anyways, it's more complex to make it generic than the accepted answer gives it credit for, so here is the complete set of code to make it work:
/**
* Returns the name of the class, as the JVM would output it. For instance, for an int, "I" is returned, for an
* array of Objects, "[Ljava/lang/Object;" is returned. If the input is null, null is returned.
*
* @param clazz
* @return
*/
public static String getJVMName(Class clazz) {
if(clazz == null) {
return null;
}
//For arrays, .getName() is fine.
if(clazz.isArray()) {
return clazz.getName().replace('.', '/');
}
if(clazz == boolean.class) {
return "Z";
} else if(clazz == byte.class) {
return "B";
} else if(clazz == short.class) {
return "S";
} else if(clazz == int.class) {
return "I";
} else if(clazz == long.class) {
return "J";
} else if(clazz == float.class) {
return "F";
} else if(clazz == double.class) {
return "D";
} else if(clazz == char.class) {
return "C";
} else {
return "L" + clazz.getName().replace('.', '/') + ";";
}
}
/**
* Generically and dynamically returns the array class type for the given class type. The dynamic equivalent of
* sending {@code String.class} and getting {@code String[].class}. Works with array types as well.
* @param clazz The class to convert to an array type.
* @return The array type of the input class.
*/
public static Class<?> getArrayClassFromType(Class<?> clazz) {
Objects.requireNonNull(clazz);
try {
return Class.forName("[" + getJVMName(clazz).replace('/', '.'));
} catch(ClassNotFoundException ex) {
// This cannot naturally happen, as we are simply creating an array type for a real type that has
// clearly already been loaded.
throw new NoClassDefFoundError(ex.getMessage());
}
}
Note that this is code from an existing library I wrote, which is why I make use of the getJVMName method. It could probably be modified to keep the dots instead of the /, but given that's how it works, I convert back and forth between the two methods. Anyhow, this works with any class, including nested array types.
Class stringArrayOfClass = String[].class;
精彩评论