动态查找表示原始 Java 类型的类

Posted

技术标签:

【中文标题】动态查找表示原始 Java 类型的类【英文标题】:Dynamically find the class that represents a primitive Java type 【发布时间】:2010-09-15 20:35:03 【问题描述】:

我需要在 Java 中进行一些反射方法调用。这些调用将包括具有原始类型(int、double 等)参数的方法。反射性查找方法时指定此类类型的方式有 int.class、double.class 等。

挑战在于我正在接受来自外部源的输入,该输入将动态指定类型。因此,我还需要动态地提出这些类引用。想象一个带有参数类型列表的方法名称列表的分隔文件:

doSomething int double
doSomethingElse java.lang.String boolean

如果输入类似于java.lang.String,我知道我可以将Class.forName("java.lang.String") 用于该类实例。有什么方法可以使用该方法或其他方法来获取原始类型 Classes 吗?

编辑: 感谢所有的受访者。显然没有内置的方法可以干净地做我想做的事,所以我将满足于重用 Spring 框架中的 ClassUtils 类。它似乎包含 Class.forName() 的替代品,可以满足我的要求。

【问题讨论】:

【参考方案1】:

原始类型的Class 实例可以按照您所说的使用例如int.class,但也可以使用 Integer.TYPE 之类的东西获得相同的值。每个原始包装类都包含一个静态字段TYPE,它具有相应的原始类实例。

您无法通过forName 获得原始类,但您可以从现成的类中获得它。如果你绝对必须使用反射,你可以试试这样:

Class clazz = Class.forName("java.lang.Integer");
Class intClass = clazz.getField("TYPE").get(null);

intClass.equals(int.class);         // => true

【讨论】:

这是真的,我不知道。但是,在我的情况下,我需要一种方法来指定可以反射调用的任何方法。因此,如果我接受 java.lang.Integer 来表示 int 值,我将失去调用接受 Integer 值的方法的能力。 你可以测试一下你是否收到了一些任意的特殊类型(比如:“int”),然后进行适当的重写。没有可以提供给 forName 的字符串值,它将检索原始类型的类。 相反,你可以只做 Integer.TYPE【参考方案2】:

Spring 框架包含一个实用程序类ClassUtils,其中包含静态方法forName。此方法可用于您描述的确切目的。

如果你不喜欢依赖 Spring:source code of the method 可以在 e 中找到。 G。 here 在他们的公共存储库中。类源代码在 Apache 2.0 模型下获得许可。

但请注意,该算法使用原始类型的硬编码映射。


编辑:感谢评论者 Dávid Horváth 和 Patrick 指出损坏的链接。

【讨论】:

ClassUtils.java 文件的源代码似乎在该链接处已损坏。 This one looks up, to me @Patrick:很遗憾,您的链接也已损坏。但是,现在可以在 GitHub 上使用 Spring 框架。 github.com/spring-projects/spring-framework/blob/master/…【参考方案3】:

您可能只需要映射原语,然后为其余的类执行“forName”方法:

我会这样做:

void someWhere()
     String methodDescription = "doSomething int double java.lang.Integer java.lang.String"
     String [] parts = methodDescription.split();
     String methodName= parts[0]
     Class [] paramsTypes = getParamTypes( parts ); // Well, not all the array, but a, sub array from 1 to arr.length..  

    Method m = someObject.class.getMethod( methodName, paramTypes );
    etc. etc etc.


public Class[] paramTypes( String [] array )
     List<Class> list = new ArrayList<Class>();
     for( String type : array ) 
         if( builtInMap.contains( type )) 
             list.add( builtInMap.get( type ) );
          else
             list.add( Class.forName( type ) );
          
     
     return list.toArray();
  

    // That's right.
Map<String,Class> builtInMap = new HashMap<String,Class>();
       builtInMap.put("int", Integer.TYPE );
       builtInMap.put("long", Long.TYPE );
       builtInMap.put("double", Double.TYPE );
       builtInMap.put("float", Float.TYPE );
       builtInMap.put("bool", Boolean.TYPE );
       builtInMap.put("char", Character.TYPE );
       builtInMap.put("byte", Byte.TYPE );
       builtInMap.put("void", Void.TYPE );
       builtInMap.put("short", Short.TYPE );

也就是说,创建一个存储基元类型的映射,如果描述属于基元,则使用映射类。此映射也可以从外部配置文件加载,以增加灵活性,因此您可以将 String 添加为内置而不是 java.lang.String 或可能具有这样的方法。

"doSomething 字符串是|否"

在 Struts、Hibernate、Spring 和 Apache libs 等 OS 项目中有大量此类代码(仅举几例),因此您无需从零开始。

顺便说一句。我没有编译上面的代码,但我很确定它只需稍加修改就可以工作,不要投反对票。

【讨论】:

这也是在 ObjectInputStream 内部完成的,它只是执行 forName() 查找,如果抛出 ClassNotFoundException,则执行映射查找。 感谢您的回复。我将在使用此查找过程的 Spring 的 ClassUtils 中重用实用程序方法。 你在哪里使用这个“低级”代码?深入了解如何保护您的服务,否则您可能会从远程客户端收到一些“System.exit()”调用,相信我服务器会关闭!【参考方案4】:

不幸的是,许多 Class 方法不能以一致的方式处理原语。在 forName 中解决这个问题的一个常见方法是有一个类似的表;

private static final Map<String, Class> BUILT_IN_MAP = 
    new ConcurrentHashMap<String, Class>();

static 
    for (Class c : new Class[]void.class, boolean.class, byte.class, char.class,  
            short.class, int.class, float.class, double.class, long.class)
        BUILT_IN_MAP.put(c.getName(), c);


public static Class forName(String name) throws ClassNotFoundException 
    Class c = BUILT_IN_MAP.get(name);
    if (c == null)
        // assumes you have only one class loader!
        BUILT_IN_MAP.put(name, c = Class.forName(name));
    return c;

【讨论】:

【参考方案5】:

以下代码讨论了如何获取字段名称已知的原始类型的类,例如在本例中为“sampleInt”。

public class CheckPrimitve 
    public static void main(String[] args) 
        Sample s = new Sample();
        try 
            System.out.println(s.getClass().getField("sampleInt").getType() == int.class); // returns true
            System.out.println(s.getClass().getField("sampleInt").getType().isPrimitive()); // returns true
         catch (NoSuchFieldException e)           
            e.printStackTrace();
         catch (SecurityException e) 
            e.printStackTrace();
               
    


class Sample 
    public int sampleInt;
    public Sample() 
        sampleInt = 10;
    

还可以通过获取给定值的相应包装类或其字段值来检查给定值是否为原始值。

    public class CheckPrimitve 
        public static void main(String[] args) 
            int i = 3;
            Object o = i;
            System.out.println(o.getClass().getSimpleName().equals("Integer")); // returns true
            Field[] fields = o.getClass().getFields();
            for(Field field:fields) 
                System.out.println(field.getType()); // returns int, int, class java.lang.Class, int
            
        
    

【讨论】:

感谢您发布答案!虽然代码 sn-p 可以回答这个问题,但添加一些附加信息仍然很棒,比如解释等..【参考方案6】:

Apache Commons Lang 有 ClassUtils.getClass(String),它支持原始类型。

【讨论】:

对我不起作用。我有一个long 类型的Field。调用 ClassUtils.getClass(field.getType().toString()) 只会返回 long 而不是 java.lang.Long【参考方案7】:

Google Guava 为这类东西提供com.google.common.primitives.Primitives

【讨论】:

【参考方案8】:

您可以使用此代码:)

/**
 * Get an array class of the given class.
 *
 * @param klass to get an array class of
 * @param <C>   the targeted class
 * @return an array class of the given class
 */
public static <C> Class<C[]> arrayClass(Class<C> klass) 
    return (Class<C[]>) Array.newInstance(klass, 0).getClass();


/**
 * Get the class that extends @link Object that represent the given class.
 *
 * @param klass to get the object class of
 * @return the class that extends Object class and represent the given class
 */
public static Class<?> objectiveClass(Class<?> klass) 
    Class<?> component = klass.getComponentType();
    if (component != null) 
        if (component.isPrimitive() || component.isArray())
            return Reflect.arrayClass(Reflect.objectiveClass(component));
     else if (klass.isPrimitive()) 
        if (klass == char.class)
            return Character.class;
        if (klass == int.class)
            return Integer.class;
        if (klass == boolean.class)
            return Boolean.class;
        if (klass == byte.class)
            return Byte.class;
        if (klass == double.class)
            return Double.class;
        if (klass == float.class)
            return Float.class;
        if (klass == long.class)
            return Long.class;
        if (klass == short.class)
            return Short.class;
    

    return klass;


/**
 * Get the class that don't extends @link Object from the given class.
 *
 * @param klass to get the non-object class of
 * @return the non-object class of the given class
 * @throws IllegalArgumentException when the given class don't have a primitive type
 */
public static Class<?> primitiveClass(Class<?> klass) 
    if (klass == Character.class)
        return char.class;
    if (klass == Integer.class)
        return int.class;
    if (klass == Boolean.class)
        return boolean.class;
    if (klass == Byte.class)
        return byte.class;
    if (klass == Double.class)
        return double.class;
    if (klass == Float.class)
        return float.class;
    if (klass == Long.class)
        return long.class;
    if (klass == Short.class)
        return short.class;

    throw new IllegalArgumentException(klass + " don't have a primitive type");

【讨论】:

以上是关于动态查找表示原始 Java 类型的类的主要内容,如果未能解决你的问题,请参考以下文章

Java反射的简单入门

Java-反射之动态加载类

java反射--动态加载

java反射总结

JVM之类加载器

设计一个表示分数的类Fraction。这个类用两个int类型的变量分别表示分子和分母。(Java语言)