Xác định xem một Đối tượng có thuộc kiểu nguyên thủy hay không


114

Tôi có một Object[]mảng và tôi đang cố gắng tìm những mảng là nguyên thủy. Tôi đã cố gắng sử dụng Class.isPrimitive(), nhưng có vẻ như tôi đang làm sai điều gì đó:

int i = 3;
Object o = i;

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

bản in java.lang.Integer, false.

Có một cách đúng hoặc một số thay thế?


12
Tóm lại: hoa int.class.isPrimitive()lợi true; Integer.class.isPrimitive()lợi tức false.
Patrick

Câu trả lời:


166

Các kiểu trong an Object[]sẽ không bao giờ thực sự là nguyên thủy - bởi vì bạn đã có tài liệu tham khảo! Ở đây kiểu của iinttrong khi kiểu của đối tượng được tham chiếu oInteger (do tự động đóng hộp).

Có vẻ như bạn cần tìm hiểu xem loại có phải là "wrapper cho nguyên thủy" hay không. Tôi không nghĩ rằng có bất kỳ thứ gì được tích hợp trong các thư viện tiêu chuẩn cho việc này, nhưng rất dễ dàng để viết mã:

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

Tôi có ấn tượng rằng nó đã hoạt động với các trình bao bọc nguyên thủy, nhưng xét cho cùng thì nó chỉ hoạt động java.lang.<type>.TYPE, tất nhiên là bản thân nó nguyên thủy. Có vẻ như tôi sẽ không thể tránh kiểm tra từng loại riêng lẻ, cảm ơn vì giải pháp tuyệt vời.
khoan3r

3
Tôi tự hỏi liệu chi phí sử dụng HashSet có thực sự tốt hơn một vài câu lệnh if hay không.
NateS

9
@NateS: Tôi tin rằng nó dễ đọc hơn, đó là lý do tại sao tôi muốn sử dụng câu lệnh đó thay vì câu lệnh "nếu" cho đến khi nó được chứng minh rằng chi phí của tập hợp là một nút cổ chai thực sự.
Jon Skeet

1
@mark: Vậy thì đó là một bối cảnh rất cụ thể và nên được xử lý như vậy. Autoboxing có áp dụng cho enums không? Không, chúng đã là loại tham chiếu. Chúng không thể nullable? Không, vì chúng là loại tham chiếu ... danh sách vẫn tiếp tục. Gọi chúng là nguyên thủy làm suy yếu ý nghĩa của thuật ngữ này một cách đáng kể, và tôi không thấy nó có ích lợi gì.
Jon Skeet

2
@NateS HashSetCho phép truy cập trong O (1) trong khi một hàng ifcâu lệnh hoặc một switchcâu lệnh yêu cầu O (# of wrappers) trong trường hợp xấu nhất. Trong thực tế, thật đáng nghi ngờ nếu các ifcâu lệnh cho số lượng cố định là 9 trình bao bọc có thể không nhanh hơn truy cập dựa trên băm.
Karl Richter

83

commons-lang ClassUtils có các phương thức liên quan .

Phiên bản mới có:

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

Các phiên bản cũ có wrapperToPrimitive(clazz)phương thức, phương thức này sẽ trả về sự tương ứng ban đầu .

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

1
Điều này đã không được thêm vào cho đến phiên bản 3.1 , liên kết của bạn phản ánh API 2.5. Tôi đã sửa nó.
javamonkey79.

8
Spring cũng có lớp ClassUtils , vì vậy nếu bạn đang sử dụng Spring thì có thể thuận tiện hơn.
Sergey


17

Đối với những người thích mã ngắn gọn.

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
Tại sao Void.class? Làm thế nào để bạn bọc một khoảng trống?
Shervin Asgari

2
@Shervin void.class.isPrimitive()trả về true
assylias

1
Void trống và giá trị hợp lệ duy nhất cho a Voidnull;) nó hữu ích để tạo a Callable<Void>là một Callable không trả về bất kỳ thứ gì.
Peter Lawrey

8

Bắt đầu từ Java 1.5 trở lên, có một tính năng mới được gọi là tự động đóng gói. Trình biên dịch tự thực hiện điều này. Khi nó nhìn thấy cơ hội, nó sẽ chuyển đổi một kiểu nguyên thủy thành lớp bao bọc thích hợp của nó.

Điều có thể xảy ra ở đây là khi bạn khai báo

Object o = i;

Trình biên dịch sẽ biên dịch câu lệnh này như nói

Object o = Integer.valueOf(i);

Đây là quyền tự động. Điều này sẽ giải thích kết quả đầu ra bạn đang nhận được. Trang này từ thông số kỹ thuật Java 1.5 giải thích chi tiết hơn về tính năng tự động đóng gói.


6
Không hoàn toàn đúng. Nó không mới là Integer, đúng hơn nó gọi là Integer.valueOf (int) để thực hiện một số bộ nhớ đệm của các cá thể Integer.
Steve Kuo

1
Integer.valueOf(int)Bản thân @SteveKuo chỉ trả về giá trị được lưu trong bộ nhớ cache khi đối số là "một byte" (đọc: giữa -128, 127, cả hai đều bao gồm). Nếu không nó sẽ gọi new Integer(int). Xem: developer.classpath.org/doc/java/lang/… , hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/…
Dragas

6

Integerkhông phải là một nguyên thủy, Class.isPrimitive()không phải là nói dối.


6

Tôi nghĩ điều này xảy ra do tự động đấm bốc .

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

Bạn có thể triển khai một phương thức tiện ích phù hợp với các lớp quyền anh cụ thể này và cung cấp cho bạn nếu một lớp nhất định là nguyên thủy.

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

Tôi thích câu trả lời này nhất vì nó sẽ nhanh hơn tra cứu băm. Ngoài ra còn có một HashSet ít hơn trong bộ nhớ (có thể là không nhiều). Cuối cùng, mọi người có thể tối ưu hóa điều này hơn nữa bằng cách sắp xếp các lớp mà những lớp được cho là thường xuyên hơn. Điều đó sẽ khác nhau trong mọi ứng dụng.
bmauter

5
Bạn có thể an toàn thay đổi .equalsthành ==. Các lớp học là những người độc thân.
Boann

5

Bạn phải đối phó với tính năng tự động đấm bốc của java.
Hãy lấy mã

kiểm tra lớp công khai
{
    public static void main (String [] args)
    {
        int i = 3;
        Đối tượng o = i;
        trở về;
    }
}
Bạn nhận được kiểm tra lớp test.class và javap -c, hãy kiểm tra mã bytecode đã tạo.
Tổng hợp từ "test.java"
kiểm tra lớp công khai mở rộng java.lang.Object {
test công khai ();
  Mã:
   0: aload_0
   1: gọi đặc biệt # 1; // Phương thức java / lang / Object. "" :() V
   4: trở lại

public static void main (java.lang.String []); Mã: 0: icont_3 1: istore_1 2: iload_1 3: bất động # 2; // Phương thức java / lang / Integer.valueOf: (I) Ljava / lang / Integer; 6: astore_2 7: trở lại

}

Như bạn có thể thấy trình biên dịch java được thêm vào
bất động # 2; // Phương thức java / lang / Integer.valueOf: (I) Ljava / lang / Integer;
để tạo một Số nguyên mới từ int của bạn và sau đó lưu trữ Đối tượng mới đó trong o qua 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

Để bạn có thể thấy rằng isPrimitive có thể trả về true (vì bạn có đủ câu trả lời cho bạn thấy lý do tại sao nó là false):

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

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

Điều này quan trọng trong việc phản ánh khi một phương thức nhận "int" chứ không phải là "Integer".

Mã này hoạt động:

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

Mã này không thành công (không thể tìm thấy phương pháp):

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

Như một số người đã nói, điều này là do autoboxing .

Bạn có thể tạo ra một phương pháp hữu ích để kiểm tra xem lớp của đối tượng là Integer, Double, vv Tuy nhiên, có không có cách nào để biết liệu một đối tượng được tạo ra bởi autoboxing một nguyên thủy ; khi nó được đóng hộp, nó trông giống như một đối tượng được tạo rõ ràng.

Vì vậy, trừ khi bạn biết chắc chắn rằng mảng của bạn sẽ không bao giờ chứa một lớp wrapper mà không có autoboxing, không có giải pháp thực sự.


2

Các loại trình bao bọc primitve sẽ không phản hồi giá trị này. Điều này là để biểu diễn lớp của các nguyên thủy, mặc dù ngoài việc phản ánh, tôi không thể nghĩ ra quá nhiều cách sử dụng nó. Ví dụ

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

in "false", nhưng

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

in "true"


2

Tôi đến trễ chương trình, nhưng nếu bạn đang thử nghiệm một lĩnh vực, bạn có thể sử dụng 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));
    }
}

Tài liệu Oracle liệt kê 8 kiểu nguyên thủy.


1

Đây là cách đơn giản nhất mà tôi có thể nghĩ ra. Các lớp trình bao bọc chỉ có trong java.langgói. Và ngoài các lớp bao bọc, không có lớp nào khác trong java.langcó trường được đặt tên TYPE. Bạn có thể sử dụng nó để kiểm tra xem một lớp có phải là lớp Wrapper hay không.

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
Tôi đồng ý. Nhưng hiện tại, đó là cách đơn giản nhất mà tôi có thể nghĩ ra. :)
Rahul Bobhate


1

bạn có thể xác định xem một đối tượng có phải là loại trình bao bọc hay không bằng các câu lệnh bên dưới:

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

và bạn cũng có thể xác định một đối tượng nguyên thủy bằng cách sử dụng phương thức 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

Đối với người dùng javapoet , cũng có cách này:

private boolean isBoxedPrimitive(Class<?> type) {
    return TypeName.get(type).isBoxedPrimitive();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.