Bestimmen, ob ein Objekt vom primitiven Typ ist

114

Ich habe ein Object[]Array und versuche, diejenigen zu finden, die primitiv sind. Ich habe versucht zu verwenden Class.isPrimitive(), aber es scheint, dass ich etwas falsch mache:

int i = 3;
Object o = i;

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

druckt java.lang.Integer, false.

Gibt es einen richtigen Weg oder eine Alternative?

Bohrer3r
quelle
12
Kurzum: int.class.isPrimitive()Erträge true; Integer.class.isPrimitive()ergibt false.
Patrick

Antworten:

165

Die Typen in einem Object[]werden niemals wirklich primitiv sein - weil Sie Referenzen haben! Hier ist die Art der iist , intwährend die Art des Objekts referenziert durch oist Integer(aufgrund der Auto-Box).

Es hört sich so an, als müssten Sie herausfinden, ob der Typ ein "Wrapper für Primitive" ist. Ich glaube nicht, dass etwas in die Standardbibliotheken eingebaut ist, aber es ist einfach zu codieren:

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
quelle
Ich hatte den Eindruck, dass es für die primitiven Wrapper funktioniert hat, aber es funktioniert schließlich nur für java.lang.<type>.TYPE, was natürlich das Primitiv selbst ist. Es scheint, dass ich es nicht vermeiden kann, jeden Typ einzeln zu prüfen, danke für die nette Lösung.
Drill3r
3
Ich frage mich, ob der Aufwand für die Verwendung von HashSet wirklich besser ist als einige if-Anweisungen.
NateS
9
@NateS: Ich glaube, es ist besser lesbar, weshalb ich diese anstelle von "if" -Anweisungen verwenden würde, bis bewiesen wurde , dass der Overhead des Sets ein tatsächlicher Engpass ist.
Jon Skeet
1
@mark: Dann ist das ein sehr spezifischer Kontext und sollte als solcher behandelt werden. Gilt Autoboxing für Aufzählungen? Nein, sie sind bereits Referenztypen. Sind sie nicht nullbar? Nein, weil es sich um Referenztypen handelt ... die Liste geht weiter. Wenn man sie Primitive nennt, wird die Bedeutung des Begriffs enorm geschwächt, und ich sehe keinen Nutzen darin.
Jon Skeet
2
@NateS Das HashSetermöglicht den Zugriff in O (1), während eine Reihe von ifAnweisungen oder switchAnweisungen im schlimmsten Fall O (Anzahl der Wrapper) erfordert. In der Praxis ist es fraglich, ob ifAnweisungen für die feste Anzahl von 9 Wrappern vielleicht doch nicht schneller sind als Hash-basierter Zugriff.
Karl Richter
82

commons-lang ClassUtils hat relevante Methoden .

Die neue Version hat:

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

Die alten Versionen haben eine wrapperToPrimitive(clazz)Methode, die die primitive Entsprechung zurückgibt .

boolean isPrimitiveOrWrapped = 
    clazz.isPrimitive() || ClassUtils.wrapperToPrimitive(clazz) != null;
Bozho
quelle
1
Dies wurde erst in Version 3.1 hinzugefügt . Ihr Link spiegelte die 2.5-API wider. Ich habe es korrigiert.
javamonkey79
8
Spring hat auch die ClassUtils- Klasse. Wenn Sie Spring bereits verwenden, kann dies bequemer sein.
Sergey
16

Für diejenigen, die knappen Code mögen.

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
quelle
1
Warum Void.class? Wie wickelt man eine Leere ein?
Shervin Asgari
2
@Shervin void.class.isPrimitive()gibt true zurück
Assylias
1
Void ist leer und der einzig gültige Wert für a Voidist null;) Es ist nützlich, Callable<Void>um ein Callable zu erstellen, das nichts zurückgibt.
Peter Lawrey
8

Ab Java 1.5 gibt es eine neue Funktion namens Auto-Boxing. Der Compiler macht das selbst. Wenn es eine Opportunity sieht, konvertiert es einen primitiven Typ in seine entsprechende Wrapper-Klasse.

Was hier wahrscheinlich passiert, ist, wenn Sie erklären

Object o = i;

Der Compiler kompiliert diese Anweisung wie folgt

Object o = Integer.valueOf(i);

Dies ist Auto-Boxing. Dies würde die Ausgabe erklären, die Sie erhalten. Diese Seite aus der Java 1.5-Spezifikation erklärt das Auto-Boxen ausführlicher.

Jose
quelle
6
Nicht ganz richtig. Es wird keine Ganzzahl neu erstellt, sondern Integer.valueOf (int) aufgerufen, wodurch die Integer-Instanzen zwischengespeichert werden.
Steve Kuo
1
@SteveKuo Integer.valueOf(int)selbst gibt nur dann einen zwischengespeicherten Wert zurück, wenn das Argument "ein Byte" ist (lesen Sie: zwischen -128, 127, beide einschließlich). Sonst ruft es an new Integer(int). Siehe: developer.classpath.org/doc/java/lang/… , hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/…
Dragas
6

Integerist kein Primitiv, Class.isPrimitive()lügt nicht.

Bombe
quelle
6

Ich denke, das passiert durch Auto-Boxen .

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

Sie können eine Dienstprogrammmethode implementieren, die diesen bestimmten Boxklassen entspricht und Ihnen angibt, ob eine bestimmte Klasse primitiv ist.

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
quelle
Diese Antwort gefällt mir am besten, weil sie schneller sein sollte als eine Hash-Suche. Es gibt auch ein HashSet weniger im Speicher (vorausgesetzt, das ist wahrscheinlich nicht viel). Schließlich könnten die Leute dies weiter optimieren, indem sie die Klassen anordnen, von denen diejenigen als häufiger wahrgenommen werden. Das wird in jeder Anwendung anders sein.
Bmauter
5
Sie können sicher ändern .equalszu ==. Klassen sind Singletons.
Boann
5

Sie müssen sich mit dem Auto-Boxen von Java befassen.
Nehmen wir den Code

öffentlicher Klassentest
{
    public static void main (String [] args)
    {
        int i = 3;
        Objekt o = i;
        Rückkehr;
    }}
}}
Sie erhalten die Klassen test.class und javap -c test , mit denen Sie den generierten Bytecode überprüfen können.
Kompiliert aus "test.java"
öffentlicher Klassentest erweitert java.lang.Object {
öffentlicher Test ();
  Code:
   0: aload_0
   1: Aufrufspezial # 1; // Methode java / lang / Object. "" :() V.
   4: zurück

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

}}

Wie Sie sehen können, wurde der Java-Compiler hinzugefügt
invokestatic # 2; // Methode java / lang / Integer.valueOf: (I) Ljava / lang / Integer;
um eine neue Ganzzahl aus Ihrem int zu erstellen und dieses neue Objekt dann über astore_2 in o zu speichern

chendral
quelle
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
quelle
3

Nur damit Sie sehen können, dass isPrimitive möglicherweise true zurückgibt (da Sie genügend Antworten haben, die Ihnen zeigen, warum es falsch ist):

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

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

Dies ist in der Reflexion wichtig, wenn eine Methode "int" anstelle einer "Ganzzahl" aufnimmt.

Dieser Code funktioniert:

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

Dieser Code schlägt fehl (die Methode kann nicht gefunden werden):

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
quelle
2

Wie bereits mehrere Leute gesagt haben, ist dies auf Autoboxing zurückzuführen .

Sie könnten eine Dienstprogramm Methode zu erstellen , ob die Klasse des Objekts zu überprüfen ist Integer, Doubleetc. Aber es gibt keine Möglichkeit zu wissen , ob ein Objekt von Autoboxing einen primitiven erstellt wurde ; Sobald es eingerahmt ist, sieht es aus wie ein explizit erstelltes Objekt.

Wenn Sie also nicht sicher sind, dass Ihr Array ohne Autoboxing niemals eine Wrapper-Klasse enthält, gibt es keine echte Lösung.

Michael Myers
quelle
2

Die primitiven Wrapper-Typen reagieren nicht auf diesen Wert. Dies ist für die Klassendarstellung von Primitiven gedacht, obwohl ich mir, abgesehen von der Reflexion, nicht allzu viele Verwendungszwecke vorstellen kann. So zum Beispiel

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

druckt "false", aber

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

druckt "wahr"

Steve B.
quelle
2

Ich bin zu spät zur Show, aber wenn Sie ein Feld testen, können Sie Folgendes verwenden 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));
    }
}

In den Oracle-Dokumenten sind die 8 primitiven Typen aufgeführt.

whistling_marmot
quelle
1

Dies ist der einfachste Weg, den ich mir vorstellen kann. Die Wrapper-Klassen sind nur im java.langPaket vorhanden. Abgesehen von den Wrapper-Klassen hat keine andere Klasse in java.langein Feld mit dem Namen TYPE. Sie können damit überprüfen, ob eine Klasse eine Wrapper-Klasse ist oder nicht.

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
quelle
1
Genau. Aber ab sofort ist es der einfachste Weg, den ich mir vorstellen kann. :)
Rahul Bobhate
1

Sie können anhand der folgenden Anweisungen feststellen, ob ein Objekt vom Typ Wrapper ist:

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

Sie können auch ein primitives Objekt mithilfe der isPrimitive () -Methode bestimmen

Luftblockade
quelle
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
quelle
0

Für Benutzer von Javapoet gibt es auch folgende Möglichkeiten:

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