Determinar se um objeto é do tipo primitivo

114

Eu tenho um Object[]array e estou tentando encontrar aqueles que são primitivos. Tentei usar Class.isPrimitive(), mas parece que estou fazendo algo errado:

int i = 3;
Object o = i;

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

impressões java.lang.Integer, false.

Existe um caminho certo ou alguma alternativa?

drill3r
fonte
12
Resumindo: int.class.isPrimitive()rendimentos true; Integer.class.isPrimitive()rendimentos false.
Patrick

Respostas:

165

Os tipos em um Object[]nunca serão realmente primitivos - porque você tem referências! Aqui, o tipo de ié, intenquanto o tipo do objeto referenciado por oéInteger (devido ao auto-boxing).

Parece que você precisa descobrir se o tipo é um "invólucro para primitivo". Não acho que haja algo integrado nas bibliotecas padrão para isso, mas é 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;
    }
}
Jon Skeet
fonte
Tive a impressão de que funcionava para os invólucros primitivos, mas só funciona para java.lang.<type>.TYPEdepois de tudo, que é claro o próprio primitivo. Parece que não vou conseguir evitar a verificação de cada tipo individualmente, obrigado pela boa solução.
drill3r
3
Eu me pergunto se a sobrecarga de usar HashSet é realmente melhor do que algumas instruções if.
NateS
9
@NateS: Acredito que seja mais legível, e é por isso que eu escolheria isso em vez de declarações "if" até que fosse provado que a sobrecarga do conjunto é um gargalo real.
Jon Skeet
1
@mark: Esse é um contexto muito específico e deve ser tratado como tal. O autoboxing se aplica a enums? Não, eles já são tipos de referência. Eles são não anuláveis? Não, porque são tipos de referência ... a lista continua. Chamá-los de primitivos enfraquece enormemente o significado do termo, e não vejo nenhum benefício nisso.
Jon Skeet
2
@NateS O HashSetpermite o acesso em O (1) enquanto uma linha de ifinstruções ou switchinstruções requer O (número de invólucros) no pior caso. Na prática, é questionável se as ifinstruções para o número fixo de 9 wrappers talvez não sejam mais rápidas do que o acesso baseado em hash.
Karl Richter
83

commons-lang ClassUtils tem métodos relevantes .

A nova versão possui:

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

As versões antigas possuem wrapperToPrimitive(clazz)método, que retornará a correspondência primitiva .

boolean isPrimitiveOrWrapped = 
    clazz.isPrimitive() || ClassUtils.wrapperToPrimitive(clazz) != null;
Bozho
fonte
1
Isso não foi adicionado até a v3.1 , seu link refletia a API 2.5. Eu corrigi isso.
javamonkey79
8
Spring também tem a classe ClassUtils , então se você já estiver usando Spring, pode ser mais conveniente.
Sergey de
16

Para quem gosta de 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);
}
Peter Lawrey
fonte
1
Por que Void.class? Como você quebra um vazio?
Shervin Asgari de
2
@Shervin void.class.isPrimitive()retorna verdadeiro
assylias
1
Void está vazio e o único valor válido para a Voidé null;) é útil para criar um Callable<Void>que é um Callable que não retorna nada.
Peter Lawrey de
8

A partir do Java 1.5 e superior, há um novo recurso chamado caixa automática. O compilador faz isso sozinho. Quando ele vê uma oportunidade, ele converte um tipo primitivo em sua classe de invólucro apropriada.

O que provavelmente está acontecendo aqui é quando você declara

Object o = i;

O compilador irá compilar esta declaração dizendo

Object o = Integer.valueOf(i);

Isso é boxe automático. Isso explicaria a saída que você está recebendo. Esta página da especificação Java 1.5 explica a boxing automática com mais detalhes.

Jose
fonte
6
Não é totalmente verdade. Não é novo um Integer, ao invés disso, ele chama Integer.valueOf (int) que faz algum cache das instâncias de Integer.
Steve Kuo,
1
O Integer.valueOf(int)próprio @SteveKuo só retorna o valor em cache quando o argumento é "um byte" (leia-se: entre -128, 127, ambos inclusive). Caso contrário, ele chama new Integer(int). Ver: developer.classpath.org/doc/java/lang/… , hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/…
Dragas
6

Integernão é um primitivo, Class.isPrimitive()não está mentindo.

Bombe
fonte
6

Acho que isso acontece devido ao auto-boxing .

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

Você pode implementar um método utilitário que corresponda a essas classes específicas de boxe e forneça se uma determinada classe é 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);
}
bruno conde
fonte
Eu gosto mais dessa resposta porque ela deve ser mais rápida do que uma pesquisa de hash. Também há um HashSet a menos na memória (dado que provavelmente não é muito). Por último, as pessoas poderiam otimizar isso ainda mais ordenando as classes pelas quais as classes são percebidas como mais frequentes. Isso será diferente em cada aplicação.
bmauter de
5
Você pode mudar .equalscom segurança para ==. As aulas são solteiros.
Boann
5

Você tem que lidar com o auto-boxing de java.
Vamos pegar o código

teste de classe pública
{
    public static void main (String [] args)
    {
        int i = 3;
        Objeto o = i;
        Retorna;
    }
}
Você obtém a classe test.class e javap -c test para inspecionar o bytecode gerado.
Compilado de "test.java"
teste de classe pública estende java.lang.Object {
teste público ();
  Código:
   0: aload_0
   1: invoca # 1 especial; // Método java / lang / Object. "" :() V
   4: retorno

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

}

Como você pode ver, o compilador java adicionado
invokestatic # 2; // Método java / lang / Integer.valueOf: (I) Ljava / lang / Integer;
para criar um novo inteiro a partir de seu int e, em seguida, armazena esse novo objeto em o via astore_2

chendral
fonte
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;
}
user3395079
fonte
3

Para que você possa ver que é possível que isPrimitive retorne verdadeiro (já que você tem respostas suficientes mostrando por que é falso):

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

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

Isso é importante na reflexão quando um método aceita "int" em vez de um "Inteiro".

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 falha (não é possível encontrar o 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)
    {
    }
}
TofuBeer
fonte
2

Como várias pessoas já disseram, isso se deve ao autoboxing .

Você poderia criar um método utilitário para verificar se a classe do objeto é Integer, Doubleetc. Mas não há como saber se um objeto foi criado por autoboxing de um primitivo ; depois de embalado, ele se parece com um objeto criado explicitamente.

Portanto, a menos que você tenha certeza de que seu array nunca conterá uma classe de wrapper sem autoboxing, não há solução real.

Michael Myers
fonte
2

Os tipos de wrapper primitivos não responderão a este valor. Isso é para representação de classe de primitivos, embora, além da reflexão, eu não possa pensar em muitos usos para isso de improviso. Então, por exemplo

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

imprime "falso", mas

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 "verdadeiro"

Steve B.
fonte
2

Estou atrasado para o show, mas se você estiver testando um campo, pode 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));
    }
}

Os documentos do Oracle listam os 8 tipos primitivos.

whistling_marmot
fonte
1

Esta é a maneira mais simples que eu poderia imaginar. As classes de wrapper estão presentes apenas no java.langpacote. E além das classes de wrapper, nenhuma outra classe java.langpossui um campo nomeado TYPE. Você pode usar isso para verificar se uma classe é classe Wrapper ou não.

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;        
}
Rahul Bobhate
fonte
1
Concordo. Mas, a partir de agora, é a maneira mais simples que eu poderia imaginar. :)
Rahul Bobhate
1

você pode determinar se um objeto é do tipo wrapper por meio das instruções abaixo:

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

e você também pode determinar um objeto primitivo usando o método isPrimitive ()

airblock
fonte
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
Arham
fonte
0

Para os usuários do javapoet , também existe esta maneira:

private boolean isBoxedPrimitive(Class<?> type) {
    return TypeName.get(type).isBoxedPrimitive();
}
KraftDurchBlumen
fonte