Определение примитивного типа объекта


114

У меня есть Object[]массив, и я пытаюсь найти примитивы. Пробовал пользоваться Class.isPrimitive(), но вроде что-то не так делаю:

int i = 3;
Object o = i;

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

печатает java.lang.Integer, false.

Есть ли правильный путь или какая-то альтернатива?


12
Вкратце: int.class.isPrimitive()урожайность true; Integer.class.isPrimitive()урожайность false.
Патрик

Ответы:


166

Типы в Object[]объекте никогда не будут примитивными - потому что у вас есть ссылки! Здесь тип iявляется в intто время как тип объекта , на который ссылается oэто Integer(из - за авто-бокс).

Похоже, вам нужно выяснить, является ли тип «оболочкой для примитива». Я не думаю, что для этого есть что-то встроенное в стандартные библиотеки, но это легко кодировать:

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

У меня создалось впечатление, что это работает для примитивных оболочек, но работает только в java.lang.<type>.TYPEконце концов, что, конечно же, является самим примитивом. Кажется, я не смогу избежать проверки каждого типа в отдельности, спасибо за хорошее решение.
Drill3r 02

3
Интересно, действительно ли накладные расходы на использование HashSet лучше, чем несколько операторов if.
NateS

9
@NateS: Я считаю, что это более читабельно, поэтому я бы использовал это вместо операторов «если», пока не будет доказано, что накладные расходы на набор являются фактическим узким местом.
Джон Скит,

1
@mark: Тогда это очень специфический контекст, и его следует рассматривать как таковой. Применяется ли автобокс к перечислениям? Нет, это уже ссылочные типы. Они не допускают значения NULL? Нет, потому что они ссылочные типы ... список можно продолжить. Называя их примитивами, мы сильно ослабляем значение этого термина, и я не вижу в этом никакой пользы.
Джон Скит

2
@NateS Разрешает HashSetдоступ в O (1), тогда как для строки ifоператоров или switchоператоров требуется O (количество оболочек) в худшем случае. На практике сомнительно, что ifоператоры для фиксированного числа из 9 оболочек в конце концов не быстрее, чем доступ на основе хешей.
Карл Рихтер

83

commons-lang ClassUtils имеет соответствующие методы .

В новой версии есть:

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

В старых версиях есть wrapperToPrimitive(clazz)метод, возвращающий примитивное соответствие.

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

1
Это не было добавлено до версии 3.1 , ваша ссылка отражала API 2.5. Я поправил.
javamonkey79

8
Spring также имеет класс ClassUtils , поэтому, если вы уже используете Spring, это может быть более удобным.
Сергей


17

Для любителей лаконичного кода.

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
Почему Void.class? Как обернуть пустоту?
Шервин Асгари

2
@Shervin void.class.isPrimitive()возвращает истину
assylias

1
Void пуст, и единственное допустимое значение для a Void- это null;) это полезно для создания Callable<Void>объекта, который является вызываемым, который ничего не возвращает.
Питер Лоури

8

Начиная с Java 1.5 и выше, появилась новая функция, называемая автоматическим боксом. Компилятор делает это сам. Когда он видит возможность, он преобразует примитивный тип в соответствующий класс-оболочку.

Вероятно, здесь происходит, когда вы объявляете

Object o = i;

Компилятор скомпилирует этот оператор как

Object o = Integer.valueOf(i);

Это автобокс. Это объяснило бы результат, который вы получаете. На этой странице из спецификации Java 1.5 более подробно объясняется автоматическая упаковка.


6
Не совсем так. Он не является новым Integer, скорее он вызывает Integer.valueOf (int), который выполняет некоторое кеширование экземпляров Integer.
Стив Куо

1
Integer.valueOf(int)Сам @SteveKuo возвращает кешированное значение только тогда, когда аргумент является «байтом» (читай: от -128 до 127, включительно). В противном случае он звонит new Integer(int). См: developer.classpath.org/doc/java/lang/... , hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/...
Драгас


6

Думаю это происходит из -за автобокса .

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

Вы можете реализовать служебный метод, который соответствует этим конкретным классам бокса и дает вам, является ли определенный класс примитивным.

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

Мне больше всего нравится этот ответ, потому что он должен быть быстрее, чем поиск по хешу. Также в памяти на один HashSet меньше (конечно, это немного). Наконец, люди могут оптимизировать это дальше, упорядочив классы, которые воспринимаются как более частые. Это будет отличаться в каждом приложении.
bmauter

5
Можете смело менять .equalsна ==. Классы одиночные.
Boann

5

Вам придется иметь дело с автобоксом java.
Возьмем код

общедоступный тест
{
    public static void main (String [] args)
    {
        int я = 3;
        Объект o = i;
        возвращение;
    }
}
Вы получаете класс test.class и javap -c test, позволяющий проверить сгенерированный байт-код.
Скомпилировано из "test.java"
открытый класс test расширяет java.lang.Object {
публичный тест ();
  Код:
   0: aload_0
   1: invokespecial # 1; // Метод java / lang / Object. "" :() V
   4: возврат

public static void main (java.lang.String []); Код: 0: iconst_3 1: istore_1 2: iload_1 3: invokestatic # 2; // Метод java / lang / Integer.valueOf: (I) Ljava / lang / Integer; 6: astore_2 7: возврат

}

Как видите, добавлен java-компилятор
invokestatic # 2; // Метод java / lang / Integer.valueOf: (I) Ljava / lang / Integer;
чтобы создать новое целое число из вашего int, а затем сохранить этот новый объект в o через 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

Просто чтобы вы могли видеть, что isPrimitive может вернуть true (поскольку у вас достаточно ответов, показывающих, почему это false):

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

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

Это имеет значение при отражении, когда метод принимает "int", а не "Integer".

Этот код работает:

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

Этот код не работает (не удается найти метод):

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

Как уже говорили несколько человек, это связано с автобоксом .

Вы могли бы создать служебный метод , чтобы проверить , является ли класс объекта Integer, Doubleи т.д. Но нет никакого способа узнать , был ли объект , созданный Autoboxing примитива ; после упаковки он выглядит как явно созданный объект.

Поэтому, если вы точно не знаете, что ваш массив никогда не будет содержать класс-оболочку без автобокса, настоящего решения нет.


2

Типы-оболочки primitve не будут реагировать на это значение. Это для представления примитивов классом, хотя, помимо отражения, я не могу придумать слишком много применений для него сразу. Так, например

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

печатает "ложь", но

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

печатает "правда"


2

Я опаздываю на выставку, но если вы тестируете поле, вы можете использовать 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));
    }
}

В документации Oracle перечислены 8 примитивных типов.


1

Это самый простой способ, который я мог придумать. Классы-оболочки присутствуют только в java.langпакете. И кроме классов-оболочек, ни один другой класс не java.langимеет поля с именем TYPE. Вы можете использовать это, чтобы проверить, является ли класс классом Wrapper или нет.

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
Я согласен. Но на данный момент это самый простой способ, который я мог придумать. :)
Рахул Бобхейт


1

вы можете определить, является ли объект типом оболочки, с помощью следующих операторов:

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

и вы также можете определить примитивный объект с помощью метода 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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.