34

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

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);

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

4

8 回答 8

45

Apache Commons Lang有一个实用方法来执行此操作(ClassUtils.primitiveToWrapper()),它在幕后同样丑陋,但至少你可以假装它很好。

于 2009-11-09T23:27:50.270 回答
43

我在回答中使用了 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() 和其他一些附带的东西。

于 2009-11-09T23:24:31.520 回答
21

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

    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() 创建一个您指定的任何类型的数组,无论它是原始的还是对象的。在对象的情况下,所有元素都是对象类型,但初始化为 null。在原始的情况下,元素是原始类型。但是原始变量/数组元素不能为空,因此它们具有原始类型的默认值,例如 int 将为零。因此没有元素会是空的。现在,如果您尝试使用 Array.get() 获取元素的值,Array.get() 将别无选择,只能将原始值框到一个对象中,例如 int 到 Integer,因为 Array.get() 可以t 返回原始值。现在您有了原始原始类型的装箱(包装)类型的对象。最后调用 Object.getClass() 为您提供装箱(包装)类型。

这个技巧适用于你现在和将来在 Java 中拥有的任何原始类型。

于 2016-11-29T18:47:57.203 回答
8

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

于 2009-11-09T23:21:51.600 回答
3
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;
}
于 2018-06-12T06:51:06.607 回答
3

所以你想得到包装类类型,好的。

概要

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

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

但相反,我们想要包装器类型。

Java中的原始类型

现在您已经发现原始类唯一有用的就是返回 true for c.isPrimitive();

来自wiki 书籍 - java 编程

原始类型是 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 替换为实例。

开心!

于 2019-07-19T09:35:58.850 回答
2

(思路)获取类名,首字母大写,然后调用Class.forInstance(className).newInstance(primitive)。例外是 "char" -> Character 和 "int" -> Integer

                                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");
                                        }
                                    }

                                }
于 2018-04-13T17:54:20.850 回答
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();
}

是我使用编写的测试,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

差异相当小。

于 2020-06-05T16:13:57.147 回答