在Java中获取包装器类类型的简单方法

时间:2009-11-09 23:18:35

标签: java reflection wrapper

我有一段代码需要在方法中传递字段的类。由于我的代码的机制,我只能处理引用对象而不是基元。我想要一种简单的方法来确定Field的类型是否是原始的,并将其与适当的包装类交换。所以在代码中我到目前为止所做的是这样的:

Field f = getTheField(); // Dummy method that returns my Field
Class<?> c = f.getType();
if (c == int.class) {
    c = Integer.class;
}
else if (c == float.class) {
    c = Float.class;
}
// etc
myMethod(c);

这很好用,除了我需要显式检查所有基本类型并用适当的包装类交换它们。现在我知道没有那么多的原始类型,简单地列出它们就不会有问题,但我想知道是否有更简单,更优雅的方式。

9 个答案:

答案 0 :(得分:37)

我在回答中使用了Google Collections Library,因为我已经被宠坏了,但是如果你愿意的话,你可以看看如何使用简单的HashMaps来实现它。

  // safe because both Long.class and long.class are of type Class<Long>
  @SuppressWarnings("unchecked")
  private static <T> Class<T> wrap(Class<T> c) {
    return c.isPrimitive() ? (Class<T>) PRIMITIVES_TO_WRAPPERS.get(c) : c;
  }

  private static final Map<Class<?>, Class<?>> PRIMITIVES_TO_WRAPPERS
    = new ImmutableMap.Builder<Class<?>, Class<?>>()
      .put(boolean.class, Boolean.class)
      .put(byte.class, Byte.class)
      .put(char.class, Character.class)
      .put(double.class, Double.class)
      .put(float.class, Float.class)
      .put(int.class, Integer.class)
      .put(long.class, Long.class)
      .put(short.class, Short.class)
      .put(void.class, Void.class)
      .build();

奇怪的是,JDK中没有任何内容存在,但实际上没有任何内容。

编辑:我完全忘记了我们发布了这个:

http://google.github.io/guava/releases/21.0/api/docs/com/google/common/primitives/Primitives.html

它有wrap()方法,加上unwrap()和其他一些附带的东西。

答案 1 :(得分:37)

Apache Commons Lang有一个实用方法来执行此操作(ClassUtils.primitiveToWrapper()),这将是一个丑陋的掩护,但至少你可以假装它很好。

答案 2 :(得分:9)

如果您不需要高度优化的代码,这是另一种方式:

    Class<?> primitive=long.class;
    Class<?> boxed=Array.get(Array.newInstance(primitive,1),0).getClass();
    System.out.println(primitive.getName());
    System.out.println(boxed.getName());

(编辑/添加说明)

首先,看看Java是否有一个方法在给定基本类型时为您提供包装类。无法找到任何。

然后,看看你是否可以在给出一个原始类型时让Java创建一个原始值(然后你可以以某种方式从中获取一个对象)。无法找到办法。

但后来发现,当给定基本类型时,您可以让Java创建一个原始值数组。然后有一个Java方法,它为您提供数组元素的包装类型的对象(它是原始的)。获得该对象后,即可获得该类型。

所以这就是整个过程的工作原理:

方法Array.newInstance()创建一个您指定的任何类型的数组,无论它是基元还是对象。在object的情况下,所有元素都是对象类型,但初始化为null。在原始的情况下,元素是原始类型。但是原始变量/数组元素不能为空,因此它们具有基本类型的默认值,例如, int将为零。因此,没有元素将为null。现在,如果你试图通过使用Array.get()来获取元素的值,那么Array.get()除了选择对象的原始值之外别无选择,例如int到Integer,因为Array.get()不能返回原始值。现在你有一个原始基本类型的装箱(包装)类型的对象。最后,调用Object.getClass()会给你装箱(包装)类型。

这个技巧适用于当今和未来Java中的任何原始类型。

答案 3 :(得分:7)

您可以调用class.isPrimitive()来了解它是否是原语,但是,没有装箱方法来转换JDK中的类。至少有一个open bug与此相关。

答案 4 :(得分:2)

(想法)获取类名并制作首字母大写,然后调用Class.forInstance(className).newInstance(primitive)。例外情况是&#34; char&#34; - &GT;角色和&#34; int&#34; - &GT;整数

                                Class c=Primitive class object
                                if (c.isPrimitive()) {
                                    if (c == char.class) {
                                        Object wrapper=new Character(primitive var);
                                    }
                                    if (c == int.class) {
                                        Object wrapper=new Integer(primitive var);
                                    }
                                    else {
                                        String name=c.getName();
                                        try {
                                            Class<?> c2=Class.forName("java.lang."+name.substring(0,1).toUpperCase()+name.substring(1,name.length()));
                                            Object wrapper=c2.getConstructor(c).newInstance(primitve_var);
                                        } catch (ClassNotFoundException ex) {
                                            System.out.println("RROR");
                                        }
                                    }

                                }

答案 5 :(得分:2)

Class<?> toWrapper(Class<?> clazz) {
    if (!clazz.isPrimitive())
        return clazz;

    if (clazz == Integer.TYPE)
        return Integer.class;
    if (clazz == Long.TYPE)
        return Long.class;
    if (clazz == Boolean.TYPE)
        return Boolean.class;
    if (clazz == Byte.TYPE)
        return Byte.class;
    if (clazz == Character.TYPE)
        return Character.class;
    if (clazz == Float.TYPE)
        return Float.class;
    if (clazz == Double.TYPE)
        return Double.class;
    if (clazz == Short.TYPE)
        return Short.class;
    if (clazz == Void.TYPE)
        return Void.class;

    return clazz;
}

答案 6 :(得分:1)

还有com.sun.beans.finder.PrimitiveWrapperMap#getType(primitiveName)。 但是当然不建议使用“com.sun”包中的类......

答案 7 :(得分:0)

所以,您要获取包装器类类型。

简介

我们正在检索一个字段,然后发现它包含原始类型。

Field f = getTheField(); // Dummy method that returns my Field
Class<?> c = f.getType();

但是我们需要包装类型。

Java中的原始类型

现在,您已经发现原始类唯一适合的事情就是为c.isPrimitive();返回true。

来自wiki books - java programming

  

原始类型是Java语言中可用的最基本的数据类型。有8个:boolean,byte,char,short,int,long,float和double。这些类型充当Java中数据处理的基础。这些类型仅用于一个目的-包含一种纯净的简单值。

尝试以其他方式使用它们,您会遭受很多伤害。

无法创建基本体的新实例。

Field f = getTheField();
Class<?> c = f.getType();
Object wrapper = c.newInstance();
//  java.lang.InstantiationException thrown: int
//        at Class.newInstance (Class.java:545) 

无法转换为原始类型。

Field f = getTheField();
Class<?> c = f.getType();
Object wrapper = c.cast(0);
//  java.lang.ClassCastException thrown: Cannot cast java.lang.Integer to int
//        at Class.cast (Class.java:3578)

可以转换为空包装器类型。是的\ o /

Field f = getTheField();
Class<?> c = f.getType();
Object wrapper = c.cast(null);

没有例外,变量wrapper的类型为class java.lang.Integer,但其值为null,可以为我们带来很多好处。

原语甚至不是从包装器继承的。

boolean isSuperClass = Integer.class.isAssignableFrom(int.class); // false

这显然不能使我们走到任何地方,而应该退后一步,看看更大的情况。

起初你没有成功...

回顾一下:自java.lang.reflect.Field被标记为final以来,我们一直在检索必须来自某个地方的字段,并且不公开任何公共构造函数。

如果我们要填补空白,它可能看起来像这样。

public class Simple {
    public static int field;

    public static void main(String[] args) {
        Field f = Simple.class.getField("field"); // Actual method that returns my Field
        Class<?> c = f.getType();
    }
}

与其与机器抗争,不如让它一起工作。基元的好处之一是它们将初始化为默认值0而不是null。让我们看看是否可以使用它。

从包装的实例中获取包装器类。

public class Simple {
    public static int field;

    public static void main(String[] args) {
        Simple boxer = new Simple();
        Field f = Simple.class.getField("field");
        Object wrapped = f.get(null);    // Integer value 0
        Class<?> c = wrapped.getClass(); // class java.lang.Integer
    }
}

这比以前容易得多,我们甚至不需要做任何事情,一切都为我们完成了。另一个不愿意与潮流背道而驰的好处。

让我们对此加以改进,重构并通过提取一种方法使其更可重用。

实施手动装箱方法。

public class Simple {
    public static int field;

    public static <T> T wrap(T t) {
        return t;
    }

    public static void main(String[] args) {
        Field f = Simple.class.getField("field");
        Class<?> c = Simple.wrap(f.get(null)).getClass(); // class java.lang.Integer
    }
}

一个简单的原始包装,而不必查看类型或使用查找表,因为Java已经可以做到了。

简单的解决方案

Field f = getTheField(); // Dummy method that returns my Field
Class<?> c = f.get(null).getClass(); 

或者如果该字段不是静态的,则可以用实例替换null。

nJoy!

答案 8 :(得分:0)

使用最新的jdk,现在这是一个单行代码:

@SuppressWarnings("unchecked")
public static <T> Class<T> wrap(Class<T> c) {
    return (Class<T>) MethodType.methodType(c).wrap().returnType();
}

@SuppressWarnings("unchecked")
public static <T> Class<T> unwrap(Class<T> c) {
    return (Class<T>) MethodType.methodType(c).unwrap().returnType();
}

Here是我使用JMH编写的测试,结果如下:

Benchmark                        Mode  Cnt   Score   Error  Units
PrimitiveToWrapper.ifStatements  avgt   30  42.112 ± 0.716  ns/op
PrimitiveToWrapper.map           avgt   30  45.018 ± 0.923  ns/op
PrimitiveToWrapper.wrap          avgt   30  52.369 ± 0.836  ns/op

差别很小。