Określanie, czy obiekt jest typu pierwotnego


114

Mam Object[]tablicę i próbuję znaleźć te, które są prymitywami. Próbowałem użyć Class.isPrimitive(), ale wygląda na to, że robię coś źle:

int i = 3;
Object o = i;

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

wydruki java.lang.Integer, false.

Czy jest właściwy sposób lub alternatywa?


12
W skrócie: int.class.isPrimitive()plony true; Integer.class.isPrimitive()plony false.
Patrick,

Odpowiedzi:


166

Typy w an Object[]nigdy nie będą naprawdę prymitywne - ponieważ masz odniesienia! Tutaj typ ijest, intpodczas gdy typ obiektu, do którego się odwołuje, ojest Integer(ze względu na automatyczne boksowanie).

Wygląda na to, że musisz dowiedzieć się, czy typ jest „opakowaniem dla prymitywów”. Nie sądzę, aby w standardowych bibliotekach było coś wbudowanego, ale łatwo to zakodować:

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

Miałem wrażenie, że zadziałało to w przypadku prymitywnych opakowań, ale w końcu działa tylko w przypadku java.lang.<type>.TYPEsamego prymitywu. Wygląda na to, że nie uda mi się uniknąć sprawdzania każdego typu osobno, dzięki za fajne rozwiązanie.
drill3r

3
Zastanawiam się, czy narzut związany z użyciem HashSet jest naprawdę lepszy niż kilka instrukcji if.
NateS

9
@NateS: Uważam, że jest bardziej czytelny, dlatego też stosowałbym to zamiast stwierdzeń „jeśli”, dopóki nie zostanie udowodnione, że koszt zestawu jest rzeczywistym wąskim gardłem.
Jon Skeet

1
@mark: W takim razie jest to bardzo specyficzny kontekst i tak powinno być traktowane. Czy autoboxing dotyczy wyliczeń? Nie, to już typy referencyjne. Czy nie mają wartości null? Nie, ponieważ są to typy referencyjne ... lista jest długa. Nazywanie ich prymitywami znacznie osłabia znaczenie tego terminu i nie widzę w tym żadnej korzyści.
Jon Skeet

2
@NateS The HashSetpozwala na dostęp w O (1), podczas gdy wiersz ifinstrukcji lub switchinstrukcji wymaga O (liczba opakowań) w najgorszym przypadku. W praktyce wątpliwe jest, czy ifinstrukcje dla ustalonej liczby 9 opakowań nie są być może w końcu szybsze niż dostęp oparty na skrócie.
Karl Richter

83

commons-lang ClassUtils ma odpowiednie metody .

Nowa wersja posiada:

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

Stare wersje mają wrapperToPrimitive(clazz)metodę, która zwraca pierwotną zgodność.

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

1
Nie zostało to dodane do wersji 3.1 , Twój link odzwierciedlał API 2.5. Poprawiłem to.
javamonkey79

8
Spring ma również klasę ClassUtils , więc jeśli już używasz Springa, może być wygodniej.
Sergey


17

Dla tych, którzy lubią zwięzły kod.

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
Dlaczego Void.class? Jak zawijasz pustkę?
Shervin Asgari

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

1
Void jest puste, a jedyną prawidłową wartością a Voidjest null;) jest to przydatne do tworzenia elementu wywoływanego, Callable<Void>który nic nie zwraca.
Peter Lawrey,

8

Począwszy od Java 1.5 i nowszych, dostępna jest nowa funkcja o nazwie auto-boxing. Kompilator robi to sam. Kiedy widzi okazję, konwertuje typ pierwotny na odpowiednią klasę opakowania.

To, co prawdopodobnie dzieje się tutaj, to deklaracja

Object o = i;

Kompilator skompiluje tę instrukcję, jak mówi

Object o = Integer.valueOf(i);

To jest auto-boks. To wyjaśniałoby otrzymane dane wyjściowe. Ta strona ze specyfikacją Java 1.5 bardziej szczegółowo wyjaśnia automatyczne boksowanie.


6
Nie do końca prawda. Nie tworzy nowego typu Integer, raczej wywołuje Integer.valueOf (int), który wykonuje pewne buforowanie instancji Integer.
Steve Kuo

1
@SteveKuo Integer.valueOf(int)samo zwraca wartość zapisaną w pamięci podręcznej tylko wtedy, gdy argumentem jest „bajt” (czytaj: od -128 do 127 włącznie). W przeciwnym razie dzwoni new Integer(int). Zobacz: developer.classpath.org/doc/java/lang/... , hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/...
Dragas

6

Integernie jest prymitywem, Class.isPrimitive()nie kłamie.


6

Myślę, że dzieje się tak z powodu auto-boksu .

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

Możesz zaimplementować metodę narzędziową, która pasuje do tych konkretnych klas boksu i podaje, czy dana klasa jest prymitywna.

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

Najbardziej podoba mi się ta odpowiedź, ponieważ powinna być szybsza niż wyszukiwanie skrótu. W pamięci jest również jeden zestaw HashSet mniej (zakładając, że prawdopodobnie nie jest dużo). Wreszcie, ludzie mogliby to jeszcze bardziej zoptymalizować, porządkując zajęcia, według których są postrzegane jako częstsze. W każdej aplikacji będzie inaczej.
bmauter

5
Możesz bezpiecznie zmienić .equalsna ==. Klasy są singletonami.
Boann

5

Musisz poradzić sobie z automatycznym boksowaniem java.
Weźmy kod

publiczny test klasowy
{
    public static void main (String [] args)
    {
        int i = 3;
        Obiekt o = i;
        powrót;
    }
}
Otrzymasz klasę test.class i javap -c test, które pozwolą Ci sprawdzić wygenerowany kod bajtowy.
Utworzono z „test.java”
public class test rozszerza java.lang.Object {
test publiczny ();
  Kod:
   0: aload_0
   1: invokespecial # 1; // Metoda java / lang / Object. "" :() V
   4: powrót

public static void main (java.lang.String []); Kod: 0: iconst_3 1: istore_1 2: iload_1 3: invokestatic # 2; // Metoda java / lang / Integer.valueOf: (I) Ljava / lang / Integer; 6: astore_2 7: powrót

}

Jak widać dodano kompilator java
invokestatic # 2; // Metoda java / lang / Integer.valueOf: (I) Ljava / lang / Integer;
aby utworzyć nową liczbę całkowitą z int, a następnie przechowuje ten nowy obiekt w o za pośrednictwem 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

Abyś mógł zobaczyć, że isPrimitive może zwrócić wartość true (ponieważ masz wystarczająco dużo odpowiedzi, które pokazują, dlaczego jest fałszywe):

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

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

Ma to znaczenie w refleksji, gdy metoda przyjmuje „int” zamiast „Integer”.

Ten kod działa:

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

Ten kod nie działa (nie można znaleźć metody):

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

Jak już kilka osób powiedziało, jest to spowodowane autoboxingiem .

Państwo mogłoby stworzyć metodę użytkową, by sprawdzić, czy klasa obiektu jest Integer, Doubleitd Ale nie ma sposobu, aby wiedzieć, czy obiekt został stworzony przez autoboxing prymitywne ; po zapakowaniu wygląda jak obiekt utworzony jawnie.

Więc jeśli nie wiesz na pewno, że twoja tablica nigdy nie będzie zawierała klasy opakowującej bez autoboxingu, nie ma prawdziwego rozwiązania.


2

Podstawowe typy opakowań nie będą reagować na tę wartość. To jest dla klasowej reprezentacji prymitywów, chociaż poza refleksją nie mogę wymyślić zbyt wielu zastosowań tego odręcznego. Na przykład

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

wypisuje „false”, ale

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

drukuje „prawda”


2

Spóźniłem się na pokaz, ale jeśli testujesz pole, możesz użyć 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));
    }
}

Dokumentacja Oracle zawiera listę 8 typów pierwotnych.


1

To najprostszy sposób, jaki mogłem wymyślić. Klasy opakowania są obecne tylko w java.langpakiecie. Poza klasami opakowującymi żadna inna klasa java.langnie ma pola o nazwie TYPE. Możesz użyć tego do sprawdzenia, czy klasa jest klasą Wrapper, czy nie.

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
Zgadzam się. Ale na razie to najprostszy sposób, jaki mogłem wymyślić. :)
Rahul Bobhate


1

możesz określić, czy obiekt jest typu opakowującego, korzystając z poniższych instrukcji:

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

a także można określić prymitywny obiekt za pomocą metody 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

Dla użytkowników javapoet jest też ten sposób:

private boolean isBoxedPrimitive(Class<?> type) {
    return TypeName.get(type).isBoxedPrimitive();
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.