Determinar si un objeto es de tipo primitivo


114

Tengo una Object[]matriz y estoy tratando de encontrar las que son primitivas. He intentado usar Class.isPrimitive(), pero parece que estoy haciendo algo mal:

int i = 3;
Object o = i;

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

impresiones java.lang.Integer, false.

¿Existe una forma correcta o alguna alternativa?


12
En resumen: int.class.isPrimitive()rendimientos true; Integer.class.isPrimitive()rendimientos false.
Patrick

Respuestas:


166

Los tipos en an Object[]nunca serán realmente primitivos, ¡porque tienes referencias! Aquí el tipo de ies intmientras que el tipo de objeto al que se hace referencia oes Integer(debido al auto-boxing).

Parece que necesita averiguar si el tipo es un "contenedor de primitivo". No creo que haya nada integrado en las bibliotecas estándar para esto, pero es fácil de codificar:

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

Tenía la impresión de que funcionó para los envoltorios primitivos, pero solo funciona java.lang.<type>.TYPEdespués de todo, que por supuesto es el primitivo en sí. Parece que no podré evitar verificar cada tipo individualmente, gracias por la buena solución.
drill3r

3
Me pregunto si la sobrecarga de usar HashSet es realmente mejor que unas pocas declaraciones if.
NateS

9
@NateS: Creo que es más legible, por lo que iría con eso en lugar de declaraciones "si" hasta que se demuestre que la sobrecarga del conjunto es un cuello de botella real.
Jon Skeet

1
@mark: Entonces ese es un contexto muy específico y debe tratarse como tal. ¿El autoboxing se aplica a las enumeraciones? No, ya son tipos de referencia. ¿Son no anulables? No, porque son tipos de referencia ... la lista continúa. Llamarlos primitivos debilita enormemente el significado del término y no veo ningún beneficio en él.
Jon Skeet

2
@NateS HashSetpermite el acceso en O (1) mientras que una fila de ifdeclaraciones o switchdeclaraciones requiere O (# de envoltorios) en el peor de los casos. En la práctica, es cuestionable si las ifdeclaraciones para el número fijo de 9 contenedores no son quizás más rápidas que el acceso basado en hash, después de todo.
Karl Richter

83

commons-lang ClassUtils tiene métodos relevantes .

La nueva versión tiene:

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

Las versiones antiguas tienen wrapperToPrimitive(clazz)método, que devolverá la correspondencia primitiva .

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

1
Esto no se agregó hasta la v3.1 , su enlace reflejaba la API 2.5. Lo he corregido.
javamonkey79

8
Spring también tiene la clase ClassUtils , por lo que si ya está usando Spring, puede ser más conveniente.
Sergey


17

Para aquellos a los que les gusta el código conciso.

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
¿Por qué Void.class? ¿Cómo envuelves un vacío?
Shervin Asgari

2
@Shervin void.class.isPrimitive()devuelve verdadero
Assylias

1
Void está vacío y es el único valor válido para un Void es null;) es útil para crear un Callable<Void>que es un Invocable que no devuelve nada.
Peter Lawrey

8

A partir de Java 1.5 en adelante, hay una nueva característica llamada auto-boxing. El compilador lo hace por sí mismo. Cuando ve una oportunidad, convierte un tipo primitivo en su clase contenedora apropiada.

Lo que probablemente está sucediendo aquí es cuando declaras

Object o = i;

El compilador compilará esta declaración diciendo

Object o = Integer.valueOf(i);

Esto es auto-boxeo. Esto explicaría la salida que está recibiendo. Esta página de la especificación Java 1.5 explica el auto-boxing con más detalle.


6
No es del todo cierto. No es nuevo un Integer, sino que llama a Integer.valueOf (int), que almacena en caché las instancias de Integer.
Steve Kuo

1
@SteveKuo en Integer.valueOf(int)sí solo devuelve el valor en caché cuando el argumento es "un byte" (leído: entre -128, 127, ambos inclusive). De lo contrario, llama new Integer(int). Ver: developer.classpath.org/doc/java/lang/… , hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/…
Dragas

6

Integerno es un primitivo, Class.isPrimitive()no está mintiendo.


6

Creo que esto sucede debido al auto-boxing .

int i = 3;
Object o = i;
o.getClass().getName(); // prints Integer

Puede implementar un método de utilidad que coincida con estas clases de boxeo específicas y le proporcione si una determinada clase es primitiva.

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

Me gusta más esta respuesta porque debería ser más rápida que una búsqueda de hash. También hay un HashSet menos en la memoria (dado que probablemente no sea mucho). Por último, la gente podría optimizar esto aún más ordenando las clases por las que se percibe que son más frecuentes. Eso será diferente en cada aplicación.
bmauter

5
Puede cambiar .equalsa ==. Las clases son singletons.
Boann

5

Tienes que lidiar con el auto-boxing de java.
Tomemos el código

prueba de clase pública
{
    public static void main (String [] args)
    {
        int i = 3;
        Objeto o = i;
        regreso;
    }
}
Obtiene la clase test.class y javap -c test le permite inspeccionar el bytecode generado.
Compilado a partir de "test.java"
prueba de clase pública extiende java.lang.Object {
prueba pública ();
  Código:
   0: aload_0
   1: invocarespecial # 1; // Método java / lang / Object. "" :() V
   4: regreso

public static void main (java.lang.String []); Código: 0: iconst_3 1: istore_1 2: iload_1 3: invocaestático # 2; // Método java / lang / Integer.valueOf: (I) Ljava / lang / Integer; 6: astore_2 7: regreso

}

Como puede ver, el compilador java agregado
invokestático # 2; // Método java / lang / Integer.valueOf: (I) Ljava / lang / Integer;
para crear un nuevo entero desde su int y luego almacena ese nuevo objeto en o a través de astore_2


5
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;
}

3

Solo para que pueda ver que es posible que isPrimitive devuelva verdadero (ya que tiene suficientes respuestas que le muestran por qué es falso):

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

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

Esto es importante en la reflexión cuando un método toma "int" en lugar de un "Integer".

Este código funciona:

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)
    {
    }
}

Este código falla (no puedo encontrar el método):

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)
    {
    }
}

2

Como ya han dicho varias personas, esto se debe al autoboxing .

Usted podría crear un método de utilidad para comprobar si la clase del objeto es Integer, Double, etc, pero no hay manera de saber si un objeto fue creado por autoboxing una primitiva ; una vez que está en caja, parece un objeto creado explícitamente.

Entonces, a menos que sepa con certeza que su matriz nunca contendrá una clase contenedora sin autoboxing, no hay una solución real.


2

Los tipos de envoltorios primitivos no responderán a este valor. Esto es para la representación de clases de primitivas, aunque aparte de la reflexión, no puedo pensar en demasiados usos para ello. Así por ejemplo

System.out.println(Integer.class.isPrimitive());

imprime "falso", pero

public static void main (String args[]) throws Exception
{
    Method m = Junk.class.getMethod( "a",null);
    System.out.println( m.getReturnType().isPrimitive());
}

public static int a()
{
    return 1;
}

imprime "verdadero"


2

Llego tarde al programa, pero si estás probando un campo, puedes usar getGenericType:

import static org.junit.Assert.*;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;

import org.junit.Test;

public class PrimitiveVsObjectTest {

    private static final Collection<String> PRIMITIVE_TYPES = 
            new HashSet<>(Arrays.asList("byte", "short", "int", "long", "float", "double", "boolean", "char"));

    private static boolean isPrimitive(Type type) {
        return PRIMITIVE_TYPES.contains(type.getTypeName());
    }

    public int i1 = 34;
    public Integer i2 = 34;

    @Test
    public void primitive_type() throws NoSuchFieldException, SecurityException {
        Field i1Field = PrimitiveVsObjectTest.class.getField("i1");
        Type genericType1 = i1Field.getGenericType();
        assertEquals("int", genericType1.getTypeName());
        assertNotEquals("java.lang.Integer", genericType1.getTypeName());
        assertTrue(isPrimitive(genericType1));
    }

    @Test
    public void object_type() throws NoSuchFieldException, SecurityException {
        Field i2Field = PrimitiveVsObjectTest.class.getField("i2");
        Type genericType2 = i2Field.getGenericType();
        assertEquals("java.lang.Integer", genericType2.getTypeName());
        assertNotEquals("int", genericType2.getTypeName());
        assertFalse(isPrimitive(genericType2));
    }
}

Los documentos de Oracle enumeran los 8 tipos primitivos.


1

Esta es la forma más sencilla que se me ocurre. Las clases contenedoras están presentes solo en java.langpackage. Y aparte de las clases contenedoras, ninguna otra clase java.langtiene un nombre de campo TYPE. Puede usar eso para verificar si una clase es una clase Wrapper o no.

public static boolean isBoxingClass(Class<?> clazz)
{
    String pack = clazz.getPackage().getName();
    if(!"java.lang".equals(pack)) 
        return false;
    try 
    {
        clazz.getField("TYPE");
    } 
    catch (NoSuchFieldException e) 
    {
        return false;
    }           
    return true;        
}

1
Estoy de acuerdo. Pero a partir de ahora, es la forma más sencilla que se me ocurre. :)
Rahul Bobhate


1

podría determinar si un objeto es de tipo contenedor mediante las siguientes declaraciones:

***objClass.isAssignableFrom(Number.class);***

y también puede determinar un objeto primitivo utilizando el método isPrimitive ()


0
public class CheckPrimitve {
    public static void main(String[] args) {
        int i = 3;
        Object o = i;
        System.out.println(o.getClass().getSimpleName().equals("Integer"));
        Field[] fields = o.getClass().getFields();
        for(Field field:fields) {
            System.out.println(field.getType());
        }
    }
}  

Output:
true
int
int
class java.lang.Class
int

0

Para los usuarios de javapoet , también existe esta forma:

private boolean isBoxedPrimitive(Class<?> type) {
    return TypeName.get(type).isBoxedPrimitive();
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.