确定一个对象是否为基本类型

132

我有一个Object[]数组,我想找出其中的原始类型(primitives)。我尝试使用Class.isPrimitive(),但似乎做错了什么:

int i = 3;
Object o = i;

System.out.println(o.getClass().getName() + ", " +
                   o.getClass().isPrimitive());

打印出java.lang.Integer, false。是否有正确的方法或其他替代方法?


15
简而言之:int.class.isPrimitive() 返回 trueInteger.class.isPrimitive() 返回 false - Patrick
18个回答

173
在一个Object[]中的数据类型永远不会是原始类型,因为你有引用!这里i的类型是int,而o所引用的对象的类型是Integer(由于自动装箱导致)。
听起来你需要找出类型是否是“原始类型的包装类”。我不认为标准库中内置了任何东西来实现这一点,但编写它很容易:
import java.util.*;

public class Test
{
    public static void main(String[] args)        
    {
        System.out.println(isWrapperType(String.class));
        System.out.println(isWrapperType(Integer.class));
    }

    private static final Set<Class<?>> WRAPPER_TYPES = getWrapperTypes();

    public static boolean isWrapperType(Class<?> clazz)
    {
        return WRAPPER_TYPES.contains(clazz);
    }

    private static Set<Class<?>> getWrapperTypes()
    {
        Set<Class<?>> ret = new HashSet<Class<?>>();
        ret.add(Boolean.class);
        ret.add(Character.class);
        ret.add(Byte.class);
        ret.add(Short.class);
        ret.add(Integer.class);
        ret.add(Long.class);
        ret.add(Float.class);
        ret.add(Double.class);
        ret.add(Void.class);
        return ret;
    }
}

我原本以为它可以适用于基本包装器,但实际上它只能适用于 java.lang.<type>.TYPE,当然这就是基本类型本身。看来我无法避免逐个检查每种类型,感谢您提供的好方法。 - drill3r
3
我不确定使用HashSet是否比使用几个if语句更加高效。 - NateS
10
我认为这样更易读,所以我会选择这种方式,而不是使用“if”语句,除非证明集合的开销真的成为了瓶颈。 - Jon Skeet
1
@mark:那么这是一个非常特定的上下文,应该作为这样处理。自动装箱适用于枚举吗?不,它们已经是引用类型了。它们是否非空?不,因为它们是引用类型...等等。将它们称为原始类型会大大削弱术语的含义,我看不到任何好处。 - Jon Skeet
2
@NateS HashSet 允许在 O(1) 的时间内访问,而一系列的 if 语句或 switch 语句在最坏情况下需要 O(# of wrappers) 的时间。实际上,对于固定数量的 9 个包装纸,使用 if 语句是否比基于哈希的访问更快还有待商榷。 - Kalle Richter
显示剩余4条评论

103

commons-lang ClassUtils 有相关的方法

新版本具有:

boolean isPrimitiveOrWrapped = 
    ClassUtils.isPrimitiveOrWrapper(object.getClass());

旧版本有 wrapperToPrimitive(clazz) 方法,它将返回相应的原始类型

boolean isPrimitiveOrWrapped = 
    clazz.isPrimitive() || ClassUtils.wrapperToPrimitive(clazz) != null;

2
这个功能直到v3.1版本才被添加(https://issues.apache.org/jira/browse/LANG-756),你提供的链接反映的是2.5 API。我已经进行了更正。 - javamonkey79
10
Spring也有ClassUtils类,因此如果您已经在使用Spring,那么它可能会更方便。该类的文档链接为:http://docs.spring.io/spring/docs/3.1.4.RELEASE/javadoc-api/org/springframework/util/ClassUtils.html#isPrimitiveOrWrapper(java.lang.Class)。 - Sergey
@Bozho ClassUtils.isPrimitiveOrWrapper对于java.lang.String返回false。 - Joy
@Joy,String既不是原始类型也不是包装过的原始类型,请参阅此处以获取所有原始数据类型的概述。当然,如果需要,您可以在代码中添加object instanceof String - Jacob van Lingen

27

4
someObject.getClass().isPrimitive() - Aquarius Power

20

对于喜欢简洁代码的人。

private static final Set<Class> WRAPPER_TYPES = new HashSet(Arrays.asList(
    Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Void.class));
public static boolean isWrapperType(Class clazz) {
    return WRAPPER_TYPES.contains(clazz);
}

1
为什么使用Void.class?如何封装一个void? - Shervin Asgari
2
@Shervin void.class.isPrimitive() 返回 true。 - assylias
2
Void是空的,而且Void的唯一有效值是null ;) 它非常有用,可以创建一个不返回任何内容的Callable<Void>。 - Peter Lawrey

9

从Java 1.5开始,引入了一个名为自动装箱(auto-boxing)的新功能。编译器自动完成这个过程。当编译器发现有机会时,它会将原始类型转换为适当的包装类。

在这里可能发生的情况是,当您声明变量时,

Object o = i;

编译器将把这个语句编译为以下内容。
Object o = Integer.valueOf(i);

这是自动装箱。这可以解释您收到的输出。 Java 1.5规范中的此页面更详细地解释了自动装箱。


6
不完全正确。它并不会创建一个新的整数,而是调用Integer.valueOf(int)方法,并对整数实例进行一些缓存处理。 - Steve Kuo
1
@SteveKuo Integer.valueOf(int) 只有在参数为“字节”(即介于-128、127之间,包括两端)时才返回缓存值。否则它会调用 new Integer(int)。请参见:http://developer.classpath.org/doc/java/lang/Integer-source.html#line.305,http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/lang/Integer.java#l829 - Dragas

7
我认为这是由于自动装箱所致。
int i = 3;
Object o = i;
o.getClass().getName(); // prints Integer

您可以实现一个实用方法,该方法匹配这些特定的装箱类并告诉您某个类是否为基本类型。
public static boolean isWrapperType(Class<?> clazz) {
    return clazz.equals(Boolean.class) || 
        clazz.equals(Integer.class) ||
        clazz.equals(Character.class) ||
        clazz.equals(Byte.class) ||
        clazz.equals(Short.class) ||
        clazz.equals(Double.class) ||
        clazz.equals(Long.class) ||
        clazz.equals(Float.class);
}

我最喜欢这个答案,因为它应该比哈希查找更快。内存中也少了一个HashSet(尽管可能不多)。最后,人们可以通过按照被认为更频繁的类别进行排序来进一步优化。这在每个应用程序中都会有所不同。 - bmauter
6
你可以安全地将.equals更改为==。这些类都是单例模式。 - Boann

7

Integer不是原始类型,Class.isPrimitive()没有说谎。


6
public static boolean isValidType(Class<?> retType)
{
    if (retType.isPrimitive() && retType != void.class) return true;
    if (Number.class.isAssignableFrom(retType)) return true;
    if (AbstractCode.class.isAssignableFrom(retType)) return true;
    if (Boolean.class == retType) return true;
    if (Character.class == retType) return true;
    if (String.class == retType) return true;
    if (Date.class.isAssignableFrom(retType)) return true;
    if (byte[].class.isAssignableFrom(retType)) return true;
    if (Enum.class.isAssignableFrom(retType)) return true;
    return false;
}

5
您需要处理Java的自动装箱。
让我们看下面的代码
public class test
{
    public static void main(String [ ] args)
    {
        int i = 3;
        Object o = i;
        return;
    }
}
您将获得test.class类,使用javap -c test命令可以检查生成的字节码。
Compiled from "test.java"
public class test extends java.lang.Object{
public test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   return

public static void main(java.lang.String[]); Code: 0: iconst_3 1: istore_1 2: iload_1 3: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 6: astore_2 7: return

}正如您所看到的,Java编译器添加了

invokestatic    #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
来从int创建一个新的Integer,然后通过astore_2将该新对象存储在o中。

3

只要你看到isPrimitive返回true的可能性(因为已经有足够多的答案向你展示它为什么是false):

public class Main
{
    public static void main(final String[] argv)
    {
        final Class clazz;

        clazz = int.class;
        System.out.println(clazz.isPrimitive());
    }
}

在反射中,当一个方法需要传入"int"而不是"Integer"时,这一点非常重要。
以下代码可行:
import java.lang.reflect.Method;

public class Main
{
    public static void main(final String[] argv)
        throws Exception
    {
        final Method method;

        method = Main.class.getDeclaredMethod("foo", int.class);
    }

    public static void foo(final int x)
    {
    }
}

这段代码失败了(无法找到该方法):

import java.lang.reflect.Method;

public class Main
{
    public static void main(final String[] argv)
        throws Exception
    {
        final Method method;

        method = Main.class.getDeclaredMethod("foo", Integer.class);
    }

    public static void foo(final int x)
    {
    }
}

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