Bir nesnenin ilkel tipte olup olmadığını belirleme


114

Bir Object[]dizim var ve ilkel olanları bulmaya çalışıyorum. Kullanmaya çalıştım Class.isPrimitive()ama görünüşe göre yanlış bir şey yapıyorum:

int i = 3;
Object o = i;

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

baskılar java.lang.Integer, false.

Doğru bir yol veya alternatif var mı?


12
Kısaca: int.class.isPrimitive()verim true; Integer.class.isPrimitive()verim false.
Patrick

Yanıtlar:


166

Bir in tipleri Object[]asla gerçekten ilkel - Eğer başvuruları var çünkü! Burada türü iolan inttarafından başvurulan nesne türü ise obir Integer(otomatik boks nedeniyle).

Türün "ilkel için sarmalayıcı" olup olmadığını öğrenmeniz gerekiyor gibi görünüyor. Bunun için standart kitaplıklarda yerleşik bir şey olduğunu sanmıyorum, ancak kodlaması kolaydır:

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

İlkel sarmalayıcılar için işe yaradığı izlenimine kapılmıştım, ancak sonuçta yalnızca işe yarıyor java.lang.<type>.TYPE, elbette ilkel olanın kendisi. Görünüşe göre her türü ayrı ayrı kontrol etmekten kaçınamayacağım, güzel çözüm için teşekkürler.
matkap3r

3
HashSet kullanmanın ek yükünün birkaç if ifadesinden gerçekten daha iyi olup olmadığını merak ediyorum.
NateS

9
@NateS: Daha okunabilir olduğuna inanıyorum, bu yüzden setin ek yükünün gerçek bir darboğaz olduğu kanıtlanana kadar "if" ifadeleri yerine bununla devam ettim .
Jon Skeet

1
@mark: O halde bu çok özel bir bağlam ve bu şekilde ele alınmalıdır. Otomatik kutulama numaralandırmalar için geçerli mi? Hayır, zaten referans türler. Boş değer atanamazlar mı? Hayır, çünkü bunlar referans türleri ... liste uzayıp gidiyor. Onlara ilkel demek, terimin anlamını büyük ölçüde zayıflatıyor ve bunda hiçbir fayda görmüyorum.
Jon Skeet

2
@NateS HashSetO (1) 'de erişime izin verirken, bir dizi ififade veya bir switchifade en kötü durumda O (sarmalayıcı sayısı) gerektirir. Pratikte if, sabit sayıdaki 9 sarmalayıcı için ifadelerin karma tabanlı erişimden daha hızlı olup olmadığı şüphelidir.
Karl Richter

83

commons-lang'ın ClassUtils ilgili yöntemleri vardır .

Yeni sürümde:

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

Eski sürümler wrapperToPrimitive(clazz), ilkel yazışmaları döndürecek yönteme sahiptir .

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

1
Bu, v3.1'e kadar eklenmedi , bağlantınız 2.5 API'yi yansıtıyordu. Düzelttim.
javamonkey79

8
Spring ayrıca ClassUtils sınıfına da sahiptir , bu nedenle zaten Spring kullanıyorsanız daha uygun olabilir.
Sergey


17

Kısa kod sevenler için.

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
Neden Void.class? Bir boşluğu nasıl sararsınız?
Shervin Asgari

2
@Shervin void.class.isPrimitive()true döndürüyor
assylias

1
Void boştur ve bir Voidis için geçerli olan tek değer null;) Callable<Void>herhangi bir şey döndürmeyen bir Çağırılabilir olanı oluşturmak için kullanışlıdır .
Peter Lawrey

8

Java 1.5 ve sonraki sürümlerden başlayarak, otomatik kutulama adı verilen yeni bir özellik bulunmaktadır. Derleyici bunu kendisi yapar. Bir fırsat gördüğünde, ilkel bir türü uygun sarmalayıcı sınıfına dönüştürür.

Muhtemelen burada olan şey, beyan ettiğiniz zamandır

Object o = i;

Derleyici bu ifadeyi şu şekilde derleyecektir:

Object o = Integer.valueOf(i);

Bu otomatik boks. Bu, aldığınız çıktıyı açıklar. Java 1.5 özelliklerinin bu sayfası otomatik kutulamayı daha ayrıntılı olarak açıklamaktadır.


6
Tamamen doğru değil. Yeni bir Tamsayı değil, Tamsayı örneklerinin bazı önbelleğe alınmasını sağlayan Integer.valueOf (int) 'i çağırır.
Steve Kuo

1
@SteveKuo'nun Integer.valueOf(int)kendisi, yalnızca bağımsız değişken "bir bayt" olduğunda önbelleğe alınmış değeri döndürür (okuma: -128, 127 arasında, her ikisi de dahil). Aksi takdirde çağırır new Integer(int). Bakınız: developer.classpath.org/doc/java/lang/… , hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/…
Dragas


6

Bunun otomatik boks nedeniyle olduğunu düşünüyorum .

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

Bu belirli boks sınıflarıyla eşleşen ve belirli bir sınıfın ilkel olup olmadığını size veren bir yardımcı yöntem uygulayabilirsiniz.

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

Bu yanıtı en çok beğendim çünkü hash aramasından daha hızlı olmalı. Ayrıca bellekte bir adet daha az HashSet vardır (muhtemelen fazla olmadığı kabul edilir). Son olarak, millet, hangi sınıfların daha sık algılanacağını sıralayarak bunu daha da optimize edebilir. Bu her uygulamada farklı olacaktır.
bmauter

5
Güvenle değiştirebilirsiniz .equalsiçin ==. Sınıflar tekildir.
Boann

5

Java'nın otomatik kutulamasıyla uğraşmak zorundasın.
Kodu alalım

genel sınıf testi
{
    public static void main (String [] değiştirgeler)
    {
        int i = 3;
        Nesne o = i;
        dönüş;
    }
}
Test.class sınıfını ve javap -c testini alırsınız, üretilen bayt kodunu inceleyelim.
"Test.java" dan derlenmiştir
public class testi, java.lang.Object {
genel test ();
  Kod:
   0: aload_0
   1: özel # 1'i çağırır; // Yöntem java / lang / Object. "" :() V
   4: dönüş

public static void main (java.lang.String []); Kod: 0: iconst_3 1: istore_1 2: iload_1 3: invokestatik # 2; // Yöntem java / lang / Integer.valueOf: (I) Ljava / lang / Tamsayı; 6: astore_2 7: dönüş

}

Gördüğünüz gibi java derleyicisi eklendi
invokestatic # 2; // Yöntem java / lang / Integer.valueOf: (I) Ljava / lang / Tamsayı;
int'inizden yeni bir Tamsayı oluşturmak ve ardından o yeni Nesneyi astore_2 aracılığıyla o depolar


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

Sırf bunun isPrimitive'nin doğruya dönmesinin mümkün olduğunu görebilmeniz için (çünkü bunun neden yanlış olduğunu gösteren yeterli cevaba sahipsiniz):

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

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

Bu, bir yöntem "Tamsayı" yerine "int" aldığında düşünmede önemlidir.

Bu kod çalışır:

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

Bu kod başarısız olur (yöntem bulunamıyor):

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

Birkaç kişinin daha önce söylediği gibi, bu otomatik kutudan kaynaklanıyor .

Sen olabilir nesnesinin sınıf olup olmadığını kontrol etmek için bir yardımcı program yöntemi oluşturmak Integer, Doublevb Ama var bir nesne ilkel Autoboxing tarafından yaratıldı olmadığını bilmenin bir yolu ; Kutulandığında, açıkça oluşturulmuş bir nesne gibi görünür.

Dolayısıyla, dizinizin otomatik kutulama olmadan asla bir sarmalayıcı sınıfı içermeyeceğinden emin değilseniz, gerçek bir çözüm yoktur.


2

İlkel sarmalayıcı türleri bu değere yanıt vermeyecektir. Bu, ilkellerin sınıf temsili içindir, ancak düşünme bir yana, onun hazırlıksız olarak çok fazla kullanımı düşünemiyorum. Yani mesela

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

"false" yazdırıyor, ancak

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

"true" yazdırır


2

Gösteriye geç kaldım, ancak bir alanı test ediyorsanız şunları kullanabilirsiniz 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 dokümanlar 8 ilkel türleri listelenmektedir.


1

Bu düşünebildiğim en basit yol. Sarmalayıcı sınıfları yalnızca java.langpakette bulunur. Ve sarmalayıcı sınıflarından ayrı olarak, içindeki başka hiçbir sınıfın java.langadı yoktur TYPE. Bunu, bir sınıfın Wrapper sınıfı olup olmadığını kontrol etmek için kullanabilirsiniz.

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
Katılıyorum. Ama şu an için düşünebildiğim en basit yol bu. :)
Rahul Bobhate


1

bir nesnenin sarmalayıcı türü olup olmadığını aşağıdaki ifadelere göre belirleyebilirsiniz:

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

ve ayrıca isPrimitive () yöntemini kullanarak ilkel bir nesneyi de belirleyebilirsiniz.


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

Javapoet kullanıcıları için şu yol da var:

private boolean isBoxedPrimitive(Class<?> type) {
    return TypeName.get(type).isBoxedPrimitive();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.