Menentukan apakah sebuah Objek berjenis primitif

114

Saya memiliki Object[]array, dan saya mencoba menemukan yang primitif. Saya sudah mencoba menggunakan Class.isPrimitive(), tetapi sepertinya saya melakukan sesuatu yang salah:

int i = 3;
Object o = i;

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

cetakan java.lang.Integer, false.

Apakah ada cara yang benar atau alternatif?

drill3r
sumber
12
Singkatnya: int.class.isPrimitive()hasil true; Integer.class.isPrimitive()hasil false.
Patrick

Jawaban:

166

Tipe dalam an Object[]tidak akan pernah benar - benar primitif - karena Anda punya referensi! Di sini jenisnya iadalah intsedangkan jenis objek yang dirujuk oadalah Integer(karena auto-boxing).

Sepertinya Anda perlu mencari tahu apakah jenisnya adalah "pembungkus untuk primitif". Saya tidak berpikir ada sesuatu yang dibangun ke dalam pustaka standar untuk ini, tetapi mudah untuk membuat kode:

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
sumber
Saya mendapat kesan bahwa itu berhasil untuk pembungkus primitif, tetapi itu hanya berfungsi untuk java.lang.<type>.TYPEsetelah semua, yang tentu saja primitif itu sendiri. Sepertinya saya tidak akan dapat menghindari pemeriksaan untuk setiap jenis secara individual, terima kasih untuk solusi yang bagus.
drill3r
3
Saya ingin tahu apakah overhead menggunakan HashSet benar-benar lebih baik daripada beberapa pernyataan if.
NateS
9
@NateS: Saya percaya ini lebih mudah dibaca, itulah mengapa saya akan menggunakan itu daripada pernyataan "jika" sampai terbukti bahwa overhead set adalah hambatan yang sebenarnya.
Jon Skeet
1
@ Mark: Maka itu adalah konteks yang sangat spesifik, dan harus diperlakukan seperti itu. Apakah autoboxing berlaku untuk enum? Tidak, mereka sudah menjadi tipe referensi. Apakah mereka tidak dapat dibatalkan? Tidak, karena itu tipe referensi ... daftarnya terus berlanjut. Menyebut mereka primitif sangat melemahkan arti istilah itu, dan saya melihat tidak ada manfaatnya.
Jon Skeet
2
@NateS The HashSetmemungkinkan akses di O (1) sedangkan deretan ifpernyataan atau switchpernyataan membutuhkan O (# pembungkus) dalam kasus terburuk. Dalam praktiknya, patut dipertanyakan jika ifpernyataan untuk jumlah tetap 9 pembungkus mungkin tidak lebih cepat daripada akses berbasis hash.
Karl Richter
83

commons-lang ClassUtils memiliki metode yang relevan .

Versi baru memiliki:

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

Versi lama memiliki wrapperToPrimitive(clazz)metode, yang akan mengembalikan korespondensi primitif .

boolean isPrimitiveOrWrapped = 
    clazz.isPrimitive() || ClassUtils.wrapperToPrimitive(clazz) != null;
Bozho
sumber
1
Ini tidak ditambahkan hingga v3.1 , tautan Anda mencerminkan 2.5 API. Saya sudah memperbaikinya.
javamonkey79
8
Spring juga memiliki class ClassUtils , jadi jika Anda sudah menggunakan Spring akan lebih nyaman.
Sergey
25

Perpustakaan Guava Google memiliki sebuah utilitas Primitif bahwa cek jika kelas adalah jenis pembungkus untuk primitif: Primitives.isWrapperType(class).

Class.isPrimitive () berfungsi untuk primitif

Andrejs
sumber
4
someObject.getClass (). isPrimitive ()
Aquarius Power
17

Bagi yang suka kode singkat.

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
sumber
1
Mengapa Void.class? Bagaimana Anda membungkus kekosongan?
Shervin Asgari
2
@Shervin void.class.isPrimitive()kembali benar
assyas
1
Void kosong dan satu-satunya nilai yang valid untuk a Voidis null;) berguna untuk membuat Callable<Void>yang merupakan Callable yang tidak mengembalikan apapun.
Peter Lawrey
8

Mulai Java 1.5 dan yang lebih baru, ada fitur baru bernama auto-boxing. Kompilator melakukan ini sendiri. Saat melihat peluang, ia mengubah tipe primitif menjadi kelas pembungkus yang sesuai.

Yang mungkin terjadi di sini adalah saat Anda menyatakan

Object o = i;

Kompilator akan mengkompilasi pernyataan ini sebagai perkataan

Object o = Integer.valueOf(i);

Ini adalah tinju otomatis. Ini akan menjelaskan keluaran yang Anda terima. Halaman ini dari spesifikasi Java 1.5 menjelaskan auto-boxing lebih detail.

Jose
sumber
6
Tidak sepenuhnya benar. Itu tidak baru Integer, melainkan memanggil Integer.valueOf (int) yang melakukan beberapa caching dari instance Integer.
Steve Kuo
1
@SteveKuo Integer.valueOf(int)sendiri hanya mengembalikan nilai yang di-cache jika argumennya adalah "satu byte" (baca: antara -128, 127, keduanya inklusif). Kalau tidak, itu panggilan new Integer(int). Lihat: developer.classpath.org/doc/java/lang/… , hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/…
Dragas
6

Integertidak primitif, Class.isPrimitive()tidak berbohong.

Bombe
sumber
6

Saya pikir ini terjadi karena tinju otomatis .

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

Anda dapat mengimplementasikan metode utilitas yang cocok dengan kelas tinju spesifik ini dan memberi tahu Anda jika kelas tertentu primitif.

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
sumber
Saya paling suka jawaban ini karena seharusnya lebih cepat daripada pencarian hash. Ada juga satu HashSet yang lebih sedikit dalam memori (mengingat itu mungkin tidak banyak). Terakhir, orang dapat mengoptimalkan ini lebih jauh dengan memesan kelas yang dianggap lebih sering. Itu akan berbeda di setiap aplikasi.
bmauter
5
Anda dapat dengan aman mengubah .equalske ==. Kelas adalah lajang.
Boann
5

Anda harus berurusan dengan auto-boxing java.
Mari kita ambil kodenya

tes kelas publik
{
    public static void main (String [] args)
    {
        int i = 3;
        Objek o = i;
        kembali;
    }
}
Anda mendapatkan kelas test.class dan tes javap -c memungkinkan Anda memeriksa bytecode yang dihasilkan.
Disusun dari "test.java"
pengujian kelas publik memperluas java.lang.Object {
tes publik ();
  Kode:
   0: aload_0
   1: invokespecial # 1; // Metode java / lang / Object. "" :() V
   4: kembali

public static void main (java.lang.String []); Kode: 0: iconst_3 1: istore_1 2: iload_1 3: invokestatic # 2; // Metode java / lang / Integer.valueOf: (I) Ljava / lang / Integer; 6: astore_2 7: kembali

}

Seperti yang Anda lihat, compiler java ditambahkan
invokestatic # 2; // Metode java / lang / Integer.valueOf: (I) Ljava / lang / Integer;
untuk membuat Integer baru dari int Anda dan kemudian menyimpan Objek baru itu di o melalui astore_2

chendral
sumber
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;
}
pengguna3395079
sumber
3

Agar Anda dapat melihat bahwa isPrimitive dapat mengembalikan true (karena Anda memiliki cukup jawaban yang menunjukkan mengapa salah):

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

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

Ini penting dalam refleksi ketika sebuah metode mengambil "int" daripada "Integer".

Kode ini berfungsi:

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

Kode ini gagal (tidak dapat menemukan metode):

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

Seperti yang telah dikatakan beberapa orang, ini karena autoboxing .

Anda bisa membuat metode utilitas untuk memeriksa apakah kelas objek adalah Integer, Double, dll Tapi ada tidak ada cara untuk mengetahui apakah obyek diciptakan oleh autoboxing primitif ; setelah dikotakkan, itu tampak seperti sebuah objek yang dibuat secara eksplisit.

Jadi, kecuali jika Anda tahu pasti bahwa array Anda tidak akan pernah berisi kelas pembungkus tanpa autoboxing, tidak ada solusi nyata.

Michael Myers
sumber
2

Jenis pembungkus primitif tidak akan menanggapi nilai ini. Ini untuk representasi kelas primitif, meskipun selain refleksi saya tidak bisa memikirkan terlalu banyak kegunaannya begitu saja. Jadi misalnya

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

mencetak "salah", tapi

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

mencetak "benar"

Steve B.
sumber
2

Saya terlambat ke pertunjukan, tetapi jika Anda menguji lapangan, Anda dapat menggunakan 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));
    }
}

Dokumen Oracle mencantumkan 8 tipe primitif.

whistling_marmot
sumber
1

Ini adalah cara paling sederhana yang dapat saya pikirkan. Kelas pembungkus hanya ada dalam java.langpaket. Dan selain dari kelas pembungkus, tidak ada kelas lain di dalam yang java.langmemiliki field bernama TYPE. Anda dapat menggunakannya untuk memeriksa apakah suatu kelas adalah kelas pembungkus atau tidak.

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
sumber
1
Saya setuju. Tapi sampai sekarang, itu adalah cara paling sederhana yang bisa saya pikirkan. :)
Rahul Bobhate
1

Anda dapat menentukan apakah suatu objek adalah tipe pembungkus dengan pernyataan di bawah ini:

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

dan Anda juga bisa menentukan objek primitif dengan menggunakan metode isPrimitive ()

blokir udara
sumber
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
sumber
0

Untuk pengguna javapoet juga ada cara ini:

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