จะกำหนดคลาสของวัตถุได้อย่างไร?


510

หากคลาสBและคลาสCขยายคลาสAและฉันมีอ็อบเจกต์ประเภทBหรือCฉันจะกำหนดประเภทที่เป็นอินสแตนซ์ได้อย่างไร


14
@starblue Casting จะเป็นสิ่งแรกที่นึกถึง ฉันสงสัยว่าตัวดำเนินการอินสแตนซ์ของจะมีอยู่หากไม่จำเป็นต้องใช้มัน
b1nary.atr0phy

@ b1nary.atr0phy คงไม่ดีถ้าจะใช้โอเปอเรเตอร์ isntanceof ก่อน หากมีการโยนให้กับประเภทเข้ากันไม่ได้ผมเชื่อว่าจะส่งผลให้ ClassCastException
committedandroider

คำตอบ:


801
if (obj instanceof C) {
//your code
}

31
มันจะมีประโยชน์ที่จะต้องทราบการตรวจสอบย้อนกลับหรือวิธีการตรวจสอบว่าวัตถุไม่ได้เป็นตัวอย่างของชั้นเรียน:if(!(obj instanceof C))
Dzhuneyt

32
ฉันเชื่อว่าวิธี getClass () เป็นคำตอบของคำถามเดิม ในกรณีนี้ (obj อินสแตนซ์ของ A) ก็จะให้ผลลัพธ์ที่ "จริง" แต่ความตั้งใจคือการหาคลาสรันไทม์ของวัตถุในภาพ หาก Parent1 ถูกขยายโดย Child1 และ Child2 ให้ลองcode Child1 child1 ต่อไปนี้= new Child1 (); Parent1 parentChild = new Child2 (); Child2 child2 = new Child2 (); (child1 อินสแตนซ์ของ Parent1); (child1 อินสแตนซ์ของ Child1); (parentChild instanceof Child2); (parentChild instanceof Parent1); (parentChild instanceof Child1); code มันอาจชัดเจนเจตนาของอินสแตนซ์ของ
Bhavesh Agarwal

ทางเลือกแน่นอนในการสร้างส่วนต่อประสาน
JohnMerlino

3
ถ้าฉันมีสองคลาสที่ใช้อินเตอร์เฟสเดียว? ฉันจะแยกคลาสที่แน่นอนของวัตถุได้อย่างไร
olyv

3
สิ่งหนึ่งที่แม้ว่ามันจะไม่ทำงานถ้า obj เป็นโมฆะ วิธีแก้ปัญหาก็คือ ParentInterface.class.isAssignableFrom (Child.class)
alexbt

351

ใช้Object.getClass () ส่งคืนประเภทรันไทม์ของวัตถุ


12
นี่คือวิธีที่คุณกำหนดคลาสของวัตถุ
AA_PV

คำตอบที่ไร้ที่ติ @
gaurav

1
ไม่แน่ใจว่าทำไมคำตอบนี้ไม่ใช่คำตอบที่ได้รับความนิยมมากที่สุด
eicksl

178

มีการนำเสนอคำตอบที่ถูกต้องหลายคำ แต่ยังมีวิธีการเพิ่มเติม: Class.isAssignableFrom()และพยายามที่จะโยนวัตถุ (ซึ่งอาจส่งClassCastException)

สรุปวิธีที่เป็นไปได้

ขอสรุปวิธีที่เป็นไปได้ในการทดสอบว่าวัตถุobjเป็นตัวอย่างของประเภทหรือไม่C:

// Method #1
if (obj instanceof C)
    ;

// Method #2
if (C.class.isInstance(obj))
    ;

// Method #3
if (C.class.isAssignableFrom(obj.getClass()))
    ;

// Method #4
try {
    C c = (C) obj;
    // No exception: obj is of type C or IT MIGHT BE NULL!
} catch (ClassCastException e) {
}

// Method #5
try {
    C c = C.class.cast(obj);
    // No exception: obj is of type C or IT MIGHT BE NULL!
} catch (ClassCastException e) {
}

ความแตกต่างในnullการจัดการ

มีความแตกต่างในการnullจัดการว่า:

  • ในสองวิธีแรกนิพจน์ประเมินfalseว่าถ้าobjเป็นnull( nullไม่ใช่ตัวอย่างของอะไร)
  • วิธีที่ 3 จะทำให้NullPointerExceptionชัดขึ้น
  • วิธีที่ 4 และ 5 ในทางตรงกันข้ามยอมรับnullเพราะnullสามารถใช้กับทุกประเภท!

ข้อควรจำ: null ไม่ใช่ตัวอย่างทุกประเภท แต่สามารถส่งไปเป็นประเภทใดก็ได้

หมายเหตุ

  • Class.getName()ไม่ควรใช้เพื่อทำการทดสอบ"is-instance-of"ถ้าวัตถุไม่ได้เป็นประเภทCแต่เป็นคลาสย่อยของมันมันอาจมีชื่อและแพคเกจที่แตกต่างกันโดยสิ้นเชิง (ดังนั้นชื่อคลาสจะไม่ตรงกัน) แต่เป็น Cยังคงเป็นประเภท
  • ด้วยเหตุผลเดียวกันมรดกClass.isAssignableFrom()ไม่สมมาตร :
    obj.getClass().isAssignableFrom(C.class)จะกลับมาfalseถ้าประเภทของการobjเป็น subclass Cของ

6
นี่เป็นบทสรุปที่ยอดเยี่ยมของข้อผิดพลาดมากมายในวิธีการที่แตกต่างกัน ขอบคุณสำหรับการเขียนที่สมบูรณ์!
Kelsin

32

คุณสามารถใช้ได้:

Object instance = new SomeClass();
instance.getClass().getName(); //will return the name (as String) (== "SomeClass")
instance.getClass(); //will return the SomeClass' Class object

HTH แต่ฉันคิดว่าส่วนใหญ่แล้วมันก็ไม่ใช่วิธีปฏิบัติที่ดีที่จะใช้มันเพื่อควบคุมโฟลว์หรืออะไรทำนองนั้น ...


ฉันใช้มันเพื่อสร้างตัวบันทึกทั่วไปดังนั้นฉันจึงส่งวัตถุไปยังตัวบันทึกและมันก็ขึ้นอยู่กับชื่อคลาสของวัตถุแทนที่จะให้แท็กบันทึกหรือสตริงบันทึกทุกครั้ง ขอบคุณ
MBH

24

การใช้วิธีการใด ๆ ที่แนะนำถือว่าเป็นกลิ่นรหัสซึ่งมีพื้นฐานมาจากการออกแบบ OO ที่ไม่ดี

หากการออกแบบของคุณเป็นสิ่งที่ดีที่คุณไม่ควรพบว่าตัวเองจำเป็นต้องใช้หรือgetClass()instanceof

วิธีการใด ๆ ที่แนะนำจะทำ แต่เพียงสิ่งที่ควรคำนึงถึงการออกแบบที่ชาญฉลาด


3
ใช่อาจเป็น 99% ของการใช้ getClass และ instanceof สามารถหลีกเลี่ยงได้ด้วยการเรียกเมธอด polymorphic
บิล Lizard

3
ฉันเห็นด้วย ในกรณีนี้ฉันกำลังทำงานกับวัตถุที่สร้างขึ้นจาก xml ตามด้วยสคีมาที่ออกแบบมาไม่ดีซึ่งฉันไม่ได้เป็นเจ้าของ
ผู้ให้บริการ

28
ไม่จำเป็น บางครั้งการแยกอินเทอร์เฟซก็ดี มีหลายครั้งที่คุณต้องการทราบว่า A เป็น B หรือไม่ แต่คุณไม่ต้องการบังคับให้ A เป็น B เนื่องจากจำเป็นต้องใช้เฉพาะ A เท่านั้นสำหรับการทำงานส่วนใหญ่ - B มีฟังก์ชันการทำงานที่เป็นทางเลือก
MetroidFan2002

8
นอกจากนี้ยังมีบางครั้งที่คุณจำเป็นต้องตรวจสอบให้แน่ใจว่าวัตถุนั้นมีระดับเดียวกันกับที่คุณเปรียบเทียบ เช่นฉันต้องการแทนที่วิธีเท่ากับของวัตถุเมื่อฉันสร้างชั้นเรียนของตัวเอง ฉันมักจะตรวจสอบวัตถุที่เข้ามาเป็นของชั้นเดียวกัน
StackOverflow

58
นอกจากนี้ฉันจะบอกว่าบางสิ่งบางอย่างไม่ดีโดยไม่ต้องอธิบายว่าทำไมหรือให้การอ้างอิงถึงกระดาษหนังสือหรือแหล่งข้อมูลอื่น ๆ ที่อธิบายถึงปัญหานั้นไม่ได้สร้างสรรค์ ดังนั้นและรู้ว่าฉันอยู่ใน StackOverflow ฉันไม่รู้ว่าทำไมผู้คนถึงตอบคำถามนี้ได้มากนัก มีบางอย่างกำลังเปลี่ยนไปที่นี่ ...
AdriánPérez

15

เราสามารถใช้การสะท้อนในกรณีนี้

objectName.getClass().getName();

ตัวอย่าง:-

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    String name = request.getClass().getName();
}

ในกรณีนี้คุณจะได้รับชื่อของคลาสที่อ็อบเจกต์ส่งผ่านไปยังHttpServletRequestตัวแปรผู้อ้างอิงของอินเทอร์เฟซ


สิ่งนี้ถูกต้อง ใช้เท่านั้นobj.getClass()จะส่งกลับ className นำหน้าด้วยคำว่าclass
x6iae

request.getClass().getName();พิมพ์แพ็คเกจทั้งหมด! พร้อมกับชื่อคลาส
shareef

13

นอกจากนี้ยังมี.isInstanceวิธีการในClassคลาส "" ถ้าคุณได้คลาสของวัตถุผ่านmyBanana.getClass()คุณสามารถดูว่าวัตถุของคุณmyAppleเป็นอินสแตนซ์ของคลาสเดียวกันกับmyBananaผ่านหรือไม่

myBanana.getClass().isInstance(myApple)

1

การตรวจสอบด้วยisinstance()จะไม่เพียงพอหากคุณต้องการทราบในเวลาทำงาน ใช้:

if(someObject.getClass().equals(C.class){
    // do something
}

0

ฉันใช้ฟังก์ชั่นการเป่าในคลาส GeneralUtils ของฉันตรวจสอบว่าอาจมีประโยชน์

    public String getFieldType(Object o) {
    if (o == null) {
        return "Unable to identify the class name";
    }
    return o.getClass().getName();
}

0

ฉันใช้ Java 8 generics เพื่อรับอินสแตนซ์ของวัตถุที่ runtime แทนที่จะต้องใช้ตัวพิมพ์สลับ

 public <T> void print(T data) {
    System.out.println(data.getClass().getName()+" => The data is " + data);
}

ผ่านประเภทข้อมูลใด ๆ และวิธีการจะพิมพ์ประเภทของข้อมูลที่คุณส่งผ่านในขณะที่เรียกมัน เช่น

    String str = "Hello World";
    int number = 10;
    double decimal = 10.0;
    float f = 10F;
    long l = 10L;
    List list = new ArrayList();
    print(str);
    print(number);
    print(decimal);
    print(f);
    print(l);
    print(list);

ต่อไปนี้เป็นผลลัพธ์

java.lang.String => The data is Hello World
java.lang.Integer => The data is 10
java.lang.Double => The data is 10.0
java.lang.Float => The data is 10.0
java.lang.Long => The data is 10
java.util.ArrayList => The data is []
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.