如何在Java中获取给定类的数组类?

32

我有一个Class变量,保存了某个类型,我需要获取一个变量来保存相应的数组类型。我目前想到的最好的方法是:

Class arrayOfFooClass = java.lang.reflect.Array.newInstance(fooClass, 0).getClass();
有没有一种方法可以在不创建新实例的情况下完成这个操作?
5个回答

39

自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 的提示。


2
他从上面开始,最终得到了我所拥有的。因此,我猜newInstance是最干净的方法来实现这个。 - Deepak Sarda
它实际上可以双向工作,但你说你不想创建一个新实例。 - Christian Strempfer
没错。实际上我在寻找一个更简洁的解决方案。并不是说我不想要新实例,而是我宁愿不要 :-) - Deepak Sarda
3
这并不需要创建一个新的Class实例。 - Johannes Kuhn
@JohannesKuhn * 手掌捂脸 * - Christian Strempfer
1
Class.forName(...)方法不仅涉及字符串操作,还涉及语法处理。当元素类型为原始类型或数组类型时,您的代码无法正常工作。通过使用getCanonicalName()而不是仅使用getName(),您使其与内部类、局部类和匿名类不兼容,否则这些类将可以正常工作。由于您没有指定类加载器,因此将使用调用者的类加载器。因此,可能找不到类甚至最终进入错误的类。而且,Class.forName(...)通常无法使用隐藏类。 - Holger

20
自Java 12以来,java.lang.Class上有arrayType()方法。通过该方法:
Class<?> arrayOfFooClass = fooClass.arrayType();

Class.arrayType()实现只是调用Arrays.newInstance(this, 0).getClass()


1
你可以使用类名获取它。 只需确保使用ClassLoader获取类。
    Class klass = yourClass;
    boolean init = wantToDoStaticOperations;
    Class.forName("[L" + klass.getName() + ";", init, klass.getClassLoader());

0

对我来说,我喜欢玩弄字符串。因此,这里提供了一种更通用的解决方案,采用该方法,仍可与任意类类型一起使用。当然,它比你的答案更复杂,但是要使其通用比接受的答案给出的信用更为复杂,因此在此提供完整的代码集以使其正常工作:

    /**
     * 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());
        }
    }

请注意,这是我编写的现有库中的代码,这就是为什么我使用了getJVMName方法的原因。它可能被修改为保留点而不是/,但考虑到它的工作方式,我将两种方法来回转换。无论如何,这适用于任何类,包括嵌套数组类型。

1
你可以使用 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() 方法,因此不需要描述符字符串来实现此目的。 - Holger

-2
Class stringArrayOfClass = String[].class;

2
我事先不知道类型,它在一个类变量中。所以你的方法行不通。 - Deepak Sarda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接