Визначення, чи об’єкт примітивного типу


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.
Патрік

Відповіді:


165

Типи в 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

3
Цікаво, чи реальне використання режиму використання HashSet справді краще, ніж кілька, якщо твердження.
NateS

9
@NateS: Я вважаю, що це читабельніше, і саме тому я б пішов з цим замість заяви "якщо", поки не буде доведено, що накладні витрати є фактичним вузьким місцем.
Джон Скіт

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

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
Це не було додано до v3.1 , ваше посилання відображало 2.5 API. Я це виправив.
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є null;) корисно для створення Callable<Void>атрибутів, які є Callable, який нічого не повертає.
Пітер Лорі

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на ==. Заняття одиночні.
Боан

5

Вам доведеться мати справу з автоматичним боксом Java.
Візьмемо код

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

public static void main (java.lang.String []); Код: 0: iconst_3 1: istore_1 2: iload_1 3: інкристатичний №2; // Метод java / lang / Integer.valueOf: (I) Ljava / lang / Integer; 6: astore_2 7: повернення

}

Як ви бачите, доданий компілятор Java
інкексатичний №2; // Метод java / lang / Integer.valueOf: (I) Ljava / lang / Integer;
щоб створити новий Integer з вашого int, а потім зберігати новий об’єкт in 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 повернути істину (оскільки у вас є достатньо відповідей, що показують, чому це неправда):

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.