Determinare se un oggetto è di tipo primitivo


114

Ho un Object[]array e sto cercando di trovare quelli che sono primitivi. Ho provato a usare Class.isPrimitive(), ma sembra che stia facendo qualcosa di sbagliato:

int i = 3;
Object o = i;

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

stampe java.lang.Integer, false.

C'è un modo giusto o un'alternativa?


12
In breve: int.class.isPrimitive()rendimenti true; Integer.class.isPrimitive()rendimenti false.
Patrick

Risposte:


166

I tipi in un Object[]non saranno mai veramente primitivi, perché hai riferimenti! Qui il tipo di iè intmentre il tipo di oggetto a cui fa riferimento oè Integer(a causa dell'auto-boxing).

Sembra che tu abbia bisogno di scoprire se il tipo è un "wrapper per primitivo". Non penso che ci sia qualcosa integrato nelle librerie standard per questo, ma è facile codificare:

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

Avevo l'impressione che funzionasse con i wrapper primitivi, ma java.lang.<type>.TYPEdopo tutto funziona solo , che ovviamente è il primitivo stesso. Sembra che non potrò evitare di controllare individualmente ogni tipo, grazie per la bella soluzione.
drill3r

3
Mi chiedo se il sovraccarico dell'utilizzo di HashSet sia davvero migliore di alcune istruzioni if.
NateS

9
@ NateS: Credo che sia più leggibile, motivo per cui preferirei le affermazioni "if" fino a quando non fosse stato dimostrato che il sovraccarico del set è un vero collo di bottiglia.
Jon Skeet

1
@mark: Allora questo è un contesto molto specifico e dovrebbe essere trattato come tale. L'autoboxing si applica alle enumerazioni? No, sono già tipi di riferimento. Non annullabili? No, perché sono tipi di riferimento ... l'elenco potrebbe continuare. Chiamarli primitivi indebolisce enormemente il significato del termine e non ne vedo alcun beneficio.
Jon Skeet

2
@NateS HashSetconsente l'accesso in O (1) mentre una riga di ifistruzioni o switchun'istruzione richiede O (numero di wrapper) nel caso peggiore. In pratica è discutibile se le ifistruzioni per il numero fisso di 9 wrapper non siano forse più veloci dell'accesso basato su hash, dopotutto.
Karl Richter

83

commons-lang ClassUtils ha metodi pertinenti .

La nuova versione ha:

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

Le vecchie versioni hanno un wrapperToPrimitive(clazz)metodo, che restituirà la corrispondenza primitiva .

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

1
Questo non è stato aggiunto fino alla v3.1 , il tuo link rifletteva l'API 2.5. L'ho corretto.
javamonkey79

8
Spring ha anche la classe ClassUtils , quindi se stai già usando Spring può essere più conveniente.
Sergey


17

Per coloro che amano il codice 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
Perché Void.class? Come avvolgi un vuoto?
Shervin Asgari

2
@Shervin void.class.isPrimitive()restituisce true
assylias

1
Void è vuoto e l'unico valore valido per a Voidè null;) è utile per creare un Callable<Void>che è un Callable che non restituisce nulla.
Peter Lawrey

8

A partire da Java 1.5 e versioni successive, è disponibile una nuova funzionalità chiamata auto-boxing. Il compilatore lo fa da solo. Quando vede un'opportunità, converte un tipo primitivo nella sua classe wrapper appropriata.

Quello che probabilmente sta accadendo qui è quando dichiari

Object o = i;

Il compilatore compilerà questa dichiarazione come detto

Object o = Integer.valueOf(i);

Questa è l'auto-boxe. Questo spiegherebbe l'output che stai ricevendo. Questa pagina delle specifiche Java 1.5 spiega l'auto-boxing più in dettaglio.


6
Non del tutto vero. Non nuova un Integer, piuttosto chiama Integer.valueOf (int) che fa un po 'di caching delle istanze Integer.
Steve Kuo

1
@SteveKuo Integer.valueOf(int)stesso restituisce il valore memorizzato nella cache solo quando l'argomento è "un byte" (leggi: tra -128, 127, entrambi inclusi). Altrimenti chiama new Integer(int). Vedi: developer.classpath.org/doc/java/lang/… , hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/…
Dragas

6

Integernon è un primitivo, Class.isPrimitive()non sta mentendo.


6

Penso che questo accada a causa dell'auto-boxing .

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

Puoi implementare un metodo di utilità che corrisponda a queste specifiche classi di boxe e ti dia se una certa 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);
}

Questa risposta mi piace di più perché dovrebbe essere più veloce di una ricerca hash. C'è anche un HashSet in meno in memoria (ammesso che probabilmente non è molto). Infine, le persone potrebbero ottimizzarlo ulteriormente ordinando le classi in base alle quali vengono percepite come più frequenti. Sarà diverso in ogni applicazione.
bmauter

5
Puoi tranquillamente passare .equalsa ==. Le classi sono singole.
Boann

5

Devi fare i conti con l'auto-boxing di java.
Prendiamo il codice

test di classe pubblica
{
    public static void main (String [] args)
    {
        int i = 3;
        Oggetto o = i;
        ritorno;
    }
}
Ottieni la classe test.class e javap -c test ti consente di ispezionare il bytecode generato.
Compilato da "test.java"
public class test estende java.lang.Object {
test pubblico ();
  Codice:
   0: aload_0
   1: invokespecial # 1; // Metodo java / lang / Object. "" :() V
   4: ritorno

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

}

Come puoi vedere aggiunto il compilatore java
invokestatic # 2; // Metodo java / lang / Integer.valueOf: (I) Ljava / lang / Integer;
per creare un nuovo intero dal tuo int e quindi memorizzare quel nuovo oggetto in o tramite 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 così puoi vedere che è possibile che isPrimitive restituisca true (dal momento che hai abbastanza risposte che ti mostrano perché è falso):

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

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

Ciò è importante in riflessione quando un metodo accetta "int" anziché un "Integer".

Questo codice funziona:

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

Questo codice non riesce (impossibile trovare il metodo):

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

Come molte persone hanno già detto, ciò è dovuto all'autoboxing .

È possibile creare un metodo di utilità per verificare se la classe dell'oggetto è Integer, Doubleecc. Ma non c'è modo di sapere se un oggetto è stato creato autoboxing una primitiva ; una volta inscatolato, sembra proprio un oggetto creato esplicitamente.

Quindi, a meno che tu non sappia per certo che il tuo array non conterrà mai una classe wrapper senza l'autoboxing, non esiste una soluzione reale.


2

I tipi wrapper primitve non risponderanno a questo valore. Questo è per la rappresentazione di classe delle primitive, anche se a parte la riflessione non riesco a pensare a molti usi per esso a priori. Quindi, per esempio

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

stampa "false", ma

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

stampa "true"


2

Sono in ritardo per lo spettacolo, ma se stai testando un campo, puoi usare 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));
    }
}

I documenti Oracle elencano gli 8 tipi primitivi.


1

Questo è il modo più semplice a cui potrei pensare. Le classi wrapper sono presenti solo in java.langpackage. E a parte le classi wrapper, nessun'altra classe java.langha un campo denominato TYPE. Potresti usarlo per verificare se una classe è classe wrapper o meno.

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
Sono d'accordo. Ma per ora, è il modo più semplice a cui potrei pensare. :)
Rahul Bobhate,


1

puoi determinare se un oggetto è di tipo wrapper da sotto le istruzioni:

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

e potresti anche determinare un oggetto primitivo usando il metodo 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

Per gli utenti di javapoet , c'è anche questo modo:

private boolean isBoxedPrimitive(Class<?> type) {
    return TypeName.get(type).isBoxedPrimitive();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.