ตัวดำเนินการ 'instanceof' ทำงานแตกต่างกันไปสำหรับอินเทอร์เฟซและคลาส


88

ฉันต้องการทราบเกี่ยวกับพฤติกรรมต่อไปนี้ของinstanceofตัวดำเนินการใน Java

interface C {}

class B {}

public class A {
    public static void main(String args[]) {
        B obj = new B();
        System.out.println(obj instanceof A);      //Gives compiler error
        System.out.println(obj instanceof C);      //Gives false as output
    }
}

ทำไมจึงเป็นเช่นนั้น? ไม่มีความสัมพันธ์ระหว่างinterface Cและclass Bแต่ให้เท็จในขณะที่obj instanceof Aให้คอมไพเลอร์ผิดพลาด?


12
หมายเหตุ: ถ้าคุณเปลี่ยนเป็นมันจะObject obj = new B()คอมไพล์
user253751

1
ข้อผิดพลาดของคอมไพเลอร์บอกอะไรคุณ?
karfau

ถ้าclass Bเป็นfinalแล้วobj instanceof Cจะไม่รวบรวมทั้งเพราะถ้าสามารถไม่มีเชื้อแล้วก็รับประกันได้ว่าจะไม่เกี่ยวข้องกับB C
jaco0646

คำตอบ:


127

เนื่องจาก Java ไม่มีการสืบทอดคลาสหลายคลาสจึงเป็นที่ทราบกันดีในระหว่างการคอมไพล์ว่าobjอ็อบเจ็กต์ประเภทBไม่สามารถเป็นประเภทย่อยAได้ ในทางกลับกันมันอาจเป็นประเภทย่อยของอินเทอร์เฟซCเช่นในกรณีนี้:

interface C {}

class B {}

class D extends B implements C {}

public class A {
    public static void main(String args[]) {
        B obj = new D();
        System.out.println(obj instanceof C);      //compiles and gives true as output  
    }
}

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

System.out.println(((Object)obj) instanceof A);      //compiles fine

1
รสชาติอื่น ๆ ของการตรวจสอบที่ไม่มีความหมายคือการใช้A.class.isAssignableFrom(obj.getClass())
David Ehrmann

ฉันสับสนเล็กน้อยกับคำอธิบายของคุณที่คุณตอบว่าJava has no multiple class inheritanceใช่ฉันเห็นด้วย แต่จะนำไปใช้อย่างไรในกรณีนี้เพราะทั้ง B หรือ A ไม่ได้ขยายขอบเขตใด ๆ ดังนั้นทำไมการสืบทอดหลายรายการที่นี่จะเป็นประโยชน์ถ้าคุณสามารถอธิบายได้?
codegasmer

@codegasmer คำตอบล่าช้า: ถ้า Java อนุญาตให้คลาสสืบทอดจากคลาสอื่น ๆ ได้หลายคลาสก็สามารถทำ "คลาส D ขยาย A, B" หรือบางอย่างจากนั้น "B obj = new D ()" และสร้าง "obj อินสแตนซ์ของ A "ในคอมไพล์คำถามเดิม (ในขณะที่ไม่มี) - ในความเป็นจริงมันมีคอมไพล์ที่ดีกว่าเพราะคาดว่าจะประเมินเป็นจริง แต่ถ้ามันไม่ได้รับอนุญาตให้เป็นทั้ง B และ A ดังนั้นนิพจน์ "obj instanceof A" โดยที่ obj ถูกกำหนดให้เป็นประเภท B นั้นถือได้ว่าเป็นเรื่องไร้สาระพอสมควร
mjwach

"ถ้าคุณยังคงต้องการที่จะมีการตรวจสอบความหมาย" - มันไม่จำเป็นต้องมีความหมายเช่นถ้าเรียนนี้จากห้องสมุดอื่น ๆ / B extends Aกรอบแล้วมีโอกาสที่คุณจะใช้รุ่นที่แตกต่างกันที่รันไทม์ที่ ในชีวิตของฉันฉันต้องทำการตรวจสอบและแคสต์แปลก ๆ เช่นเดียว(A)(Object)bกับในรันไทม์มันเป็นไปได้ที่จะเป็นจริง
GotoFinal

1

ด้วยการใช้โมดิfinalฟายเออร์ในการประกาศคลาสด้านล่างนี้จะรับประกันได้ว่าจะไม่มีคลาสย่อยTestซึ่งอาจใช้อินเทอร์เฟซFoobarได้ ในกรณีนี้จะเห็นได้ชัดว่าTestและFoobarเข้ากันไม่ได้:

public final class Test {

    public static void main(String[] args) {
        Test test = new Test();
        System.out.println(test instanceof Foobar); // Compiler error: incompatible types
    }
}

interface Foobar {
}

มิฉะนั้นหากTestไม่มีการประกาศfinalอาจเป็นไปได้ว่าคลาสย่อยของTestการใช้งานอินเทอร์เฟซ และนั่นคือเหตุผลที่คอมไพเลอร์ยอมให้มีคำสั่งtest instanceof Foobarในกรณีนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.