我有一个Class
变量,保存了某个类型,我需要获取一个变量来保存相应的数组类型。我目前想到的最好的方法是:
Class arrayOfFooClass = java.lang.reflect.Array.newInstance(fooClass, 0).getClass();
有没有一种方法可以在不创建新实例的情况下完成这个操作?我有一个Class
变量,保存了某个类型,我需要获取一个变量来保存相应的数组类型。我目前想到的最好的方法是:
Class arrayOfFooClass = java.lang.reflect.Array.newInstance(fooClass, 0).getClass();
有没有一种方法可以在不创建新实例的情况下完成这个操作?自Java 12版本开始
Class
提供了一个arrayType()
方法,该方法返回其组件类型由给定的Class描述的数组类型类。请注意,各个JDK仍可能创建该类的实例³。
Class<?> stringArrayClass = FooBar.arrayType()
Java 12之前
如果您不想创建一个实例,可以手动创建数组的规范名称并通过名称获取类:
// Replace `String` by your object type.
Class<?> stringArrayClass = Class.forName(
"[L" + String.class.getCanonicalName() + ";"
);
但Jakob Jenkov在他的博客中认为你的解决方案更好,因为它不需要与字符串打交道。
Class<?> stringArrayClass = Array.newInstance(String.class, 0).getClass();
³ 感谢 Johannes Kuhn 的提示。
arrayType()
方法。通过该方法:Class<?> arrayOfFooClass = fooClass.arrayType();
Class.arrayType()
的实现只是调用Arrays.newInstance(this, 0).getClass()
。
Class klass = yourClass;
boolean init = wantToDoStaticOperations;
Class.forName("[L" + klass.getName() + ";", init, klass.getClassLoader());
对我来说,我喜欢玩弄字符串。因此,这里提供了一种更通用的解决方案,采用该方法,仍可与任意类类型一起使用。当然,它比你的答案更复杂,但是要使其通用比接受的答案给出的信用更为复杂,因此在此提供完整的代码集以使其正常工作:
/**
* 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());
}
}
if(clazz.isPrimitive()) switch(clazz.getName()) { case "boolean": return "Z"; … }
代替这个 if … else if … else if … else if …
的阶梯结构。或者用 MethodType.methodType(clazz).toMethodDescriptorString().substring(2)
替换整个 getJVMName
方法的代码。从 JDK 12 开始,Class
本身具有 descriptorString()
,这可能对你的库有用,但由于 JDK 12 还具有 arrayType()
方法,因此不需要描述符字符串来实现此目的。 - HolgerClass stringArrayOfClass = String[].class;
newInstance
是最干净的方法来实现这个。 - Deepak SardaClass.forName(...)
方法不仅涉及字符串操作,还涉及语法处理。当元素类型为原始类型或数组类型时,您的代码无法正常工作。通过使用getCanonicalName()
而不是仅使用getName()
,您使其与内部类、局部类和匿名类不兼容,否则这些类将可以正常工作。由于您没有指定类加载器,因此将使用调用者的类加载器。因此,可能找不到类甚至最终进入错误的类。而且,Class.forName(...)
通常无法使用隐藏类。 - Holger