Déterminer si un objet est de type primitif

114

J'ai un Object[]tableau et j'essaie de trouver ceux qui sont primitifs. J'ai essayé d'utiliser Class.isPrimitive(), mais il semble que je fais quelque chose de mal:

int i = 3;
Object o = i;

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

impressions java.lang.Integer, false.

Y a-t-il une bonne façon ou une alternative?

drill3r
la source
12
En bref: les int.class.isPrimitive()rendements true; Integer.class.isPrimitive()rendements false.
Patrick

Réponses:

166

Les types dans un Object[]ne seront jamais vraiment primitifs - car vous avez des références! Ici, le type de iest intalors que le type de l'objet référencé par oest Integer(dû à l'auto-boxing).

Il semble que vous ayez besoin de savoir si le type est un "wrapper pour primitive". Je ne pense pas qu'il y ait quoi que ce soit intégré dans les bibliothèques standard pour cela, mais c'est facile à coder:

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
la source
J'avais l'impression que cela fonctionnait pour les emballages primitifs, mais cela ne fonctionne java.lang.<type>.TYPEqu'après tout, ce qui est bien sûr le primitif lui-même. Il semble que je ne pourrai pas éviter de vérifier chaque type individuellement, merci pour la bonne solution.
drill3r
3
Je me demande si la surcharge de l'utilisation de HashSet est vraiment meilleure que quelques instructions if.
NateS
9
@NateS: Je pense que c'est plus lisible, c'est pourquoi j'irais avec cela au lieu des déclarations "si" jusqu'à ce qu'il soit prouvé que la surcharge de l'ensemble est un véritable goulot d'étranglement.
Jon Skeet
1
@mark: Alors c'est un contexte très spécifique, et devrait être traité comme tel. L'autoboxing s'applique-t-il aux énumérations? Non, ce sont déjà des types de référence. Sont-ils non annulables? Non, car ce sont des types de référence ... la liste est longue. Les appeler primitifs affaiblit énormément le sens du terme, et je n'y vois aucun avantage.
Jon Skeet
2
@NateS Le HashSetpermet l'accès en O (1) alors qu'une ligne d' ifinstructions ou une switchinstruction nécessite O (# de wrappers) dans le pire des cas. En pratique, il est douteux que les ifinstructions pour le nombre fixe de 9 wrappers ne soient peut-être pas plus rapides que l'accès basé sur le hachage après tout.
Karl Richter
83

commons-lang ClassUtils a des méthodes pertinentes .

La nouvelle version a:

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

Les anciennes versions ont la wrapperToPrimitive(clazz)méthode, qui retournera la correspondance primitive .

boolean isPrimitiveOrWrapped = 
    clazz.isPrimitive() || ClassUtils.wrapperToPrimitive(clazz) != null;
Bozho
la source
1
Cela n'a été ajouté qu'à la v3.1 , votre lien reflétait l'API 2.5. Je l'ai corrigé.
javamonkey79
8
Spring a également la classe ClassUtils , donc si vous utilisez déjà Spring, cela peut être plus pratique.
Sergey
25

La bibliothèque goyave de Google dispose d' un utilitaire Primitives qui contrôle si une classe est un type d'emballage pour une primitive: Primitives.isWrapperType(class).

Class.isPrimitive () fonctionne pour les primitives

Andrejs
la source
4
someObject.getClass (). isPrimitive ()
Aquarius Power
17

Pour ceux qui aiment le code laconique.

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
la source
1
Pourquoi Void.class? Comment envelopper un vide?
Shervin Asgari
2
@Shervin void.class.isPrimitive()renvoie vrai
Assylias
1
Void est vide et la seule valeur valide pour a Voidest null;) il est utile pour créer un Callable<Void>qui est un Callable qui ne renvoie rien.
Peter Lawrey
8

À partir de Java 1.5 et plus, il existe une nouvelle fonctionnalité appelée auto-boxing. Le compilateur le fait lui-même. Lorsqu'il voit une opportunité, il convertit un type primitif en sa classe wrapper appropriée.

Ce qui se passe probablement ici, c'est lorsque vous déclarez

Object o = i;

Le compilateur compilera cette déclaration comme disant

Object o = Integer.valueOf(i);

C'est de la boxe automatique. Cela expliquerait la sortie que vous recevez. Cette page de la spécification Java 1.5 explique plus en détail l'auto-boxing.

José
la source
6
Pas totalement vrai. Il ne crée pas de nouvel Integer, mais appelle Integer.valueOf (int) qui met en cache les instances Integer.
Steve Kuo le
1
@SteveKuo Integer.valueOf(int)lui-même ne renvoie la valeur mise en cache que lorsque l'argument est "un octet" (lire: entre -128, 127, les deux inclus). Sinon, ça appelle new Integer(int). Voir: developer.classpath.org/doc/java/lang/… , hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/…
Dragas
6

Integern'est pas un primitif, Class.isPrimitive()ne ment pas.

Bombe
la source
6

Je pense que cela est dû à la boxe automatique .

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

Vous pouvez implémenter une méthode utilitaire qui correspond à ces classes de boxe spécifiques et vous indique si une certaine classe est primitive.

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
la source
J'aime mieux cette réponse car elle devrait être plus rapide qu'une recherche de hachage. Il y a aussi un HashSet de moins en mémoire (ce n'est probablement pas beaucoup). Enfin, les gens pourraient optimiser davantage cela en ordonnant les classes par lesquelles celles sont perçues comme étant les plus fréquentes. Ce sera différent dans chaque application.
bmauter
5
Vous pouvez en toute sécurité changer .equalsà ==. Les classes sont des singletons.
Boann
5

Vous devez gérer l'auto-boxing de java.
Prenons le code

test de classe publique
{
    public static void main (String [] args)
    {
        int i = 3;
        Objet o = i;
        revenir;
    }
}
Vous obtenez la classe test.class et javap -c test vous permet d'inspecter le bytecode généré.
Compilé à partir de "test.java"
le test de classe publique étend java.lang.Object {
test public ();
  Code:
   0: aload_0
   1: invokespecial # 1; // Méthode java / lang / Object. "" :() V
   4: retour

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

}

Comme vous pouvez le voir, le compilateur java ajouté
invokestatic # 2; // Méthode java / lang / Integer.valueOf: (I) Ljava / lang / Integer;
pour créer un nouvel entier à partir de votre int, puis stocke ce nouvel objet dans o via astore_2

chendral
la source
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
la source
3

Juste pour que vous puissiez voir qu'il est possible pour isPrimitive de retourner true (puisque vous avez suffisamment de réponses vous montrant pourquoi il est faux):

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

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

Ceci est important dans la réflexion lorsqu'une méthode prend "int" plutôt qu'un "Integer".

Ce code fonctionne:

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

Ce code échoue (impossible de trouver la méthode):

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)
    {
    }
}
TofuBière
la source
2

Comme plusieurs personnes l'ont déjà dit, cela est dû à l' auto-boxe .

Vous pouvez créer une méthode utilitaire pour vérifier si la classe de l'objet est Integer, Doubleetc. Mais il n'y a aucun moyen de savoir si un objet a été créé par autoboxing d'une primitive ; une fois qu'il est encadré, il ressemble à un objet créé explicitement.

Donc, à moins que vous ne sachiez avec certitude que votre tableau ne contiendra jamais de classe wrapper sans autoboxing, il n'y a pas de vraie solution.

Michael Myers
la source
2

Les types de wrapper primitve ne répondront pas à cette valeur. C'est pour la représentation de classe des primitives, bien que mis à part la réflexion, je ne peux pas penser à trop d'utilisations pour cela à la légère. Donc, par exemple

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

affiche «faux», mais

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

Steve B.
la source
2

Je suis en retard au salon, mais si vous testez un champ, vous pouvez utiliser 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));
    }
}

Les documents Oracle répertorient les 8 types primitifs.

sifflement_marmot
la source
1

C'est le moyen le plus simple auquel je puisse penser. Les classes wrapper sont présentes uniquement dans java.langpackage. Et à part les classes wrapper, aucune autre classe dans java.langn'a de champ nommé TYPE. Vous pouvez l'utiliser pour vérifier si une classe est une classe Wrapper ou non.

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
la source
1
Je suis d'accord. Mais à partir de maintenant, c'est le moyen le plus simple auquel je puisse penser. :)
Rahul Bobhate
1

vous pouvez déterminer si un objet est de type wrapper en suivant des instructions:

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

et vous pouvez également déterminer un objet primitif en utilisant la méthode isPrimitive ()

airblock
la source
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
la source
0

Pour les utilisateurs de javapoet , il existe également cette méthode:

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