ชั้นในภายในอินเทอร์เฟซ


100

เป็นไปได้ไหมที่จะสร้างคลาสภายในภายในอินเทอร์เฟซ
ถ้าเป็นไปได้เหตุใดเราจึงต้องการสร้างคลาสภายในเช่นนั้นเนื่องจากเราจะไม่สร้างวัตถุอินเทอร์เฟซใด ๆ

ชั้นเรียนภายในเหล่านี้ช่วยในกระบวนการพัฒนาหรือไม่?

คำตอบ:


51

ได้คุณสามารถสร้างทั้งคลาสที่ซ้อนกันหรือคลาสภายในภายในอินเทอร์เฟซ Java (โปรดทราบว่าในทางตรงกันข้ามกับความเชื่อที่เป็นที่นิยมไม่มีสิ่งที่เรียกว่า " คลาสภายในแบบคงที่ " สิ่งนี้ไม่สมเหตุสมผลไม่มีอะไร "ภายใน" และไม่ใช่ " outter "class เมื่อคลาสที่ซ้อนกันเป็นแบบคงที่ดังนั้นจึงไม่สามารถเป็น" static inner ")

อย่างไรก็ตามการคอมไพล์ต่อไปนี้ดี:

public interface A {
    class B {
    }
}

ฉันเคยเห็นมันเคยใส่ "ตัวตรวจสอบสัญญา" บางประเภทไว้ในคำจำกัดความของอินเทอร์เฟซโดยตรง (ในคลาสที่ซ้อนอยู่ในอินเทอร์เฟซที่สามารถมีวิธีการคงที่ขัดแย้งกับอินเทอร์เฟซเองซึ่งทำไม่ได้) มองแบบนี้ถ้าจำไม่ผิด

public interface A {
    static class B {
        public static boolean verifyState( A a ) {
            return (true if object implementing class A looks to be in a valid state)
        }
    }
}

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

ตอนนี้ฉันจะไม่แสดงความคิดเห็นเกี่ยวกับประโยชน์ของโครงสร้างดังกล่าวและจากที่ฉันเคยเห็นฉันเคยเห็นมาแล้ว แต่มันไม่ใช่โครงสร้างทั่วไป

200KLOC codebase ที่นี่ซึ่งสิ่งนี้เกิดขึ้นตรงเวลาเป็นศูนย์ (แต่จากนั้นเราก็มีสิ่งอื่น ๆ อีกมากมายที่เราพิจารณาการปฏิบัติที่ไม่ดีซึ่งเกิดขึ้นเป็นศูนย์เช่นกันซึ่งคนอื่น ๆ จะพบว่าปกติอย่างสมบูรณ์แบบ ... )


เพิ่มตัวอย่างการใช้งานได้ไหม ฉันเคยทดสอบสิ่งที่คล้ายกันมาแล้วและไม่เข้าใจว่าฉันจะได้อะไรจากการใช้โครงสร้างนี้
โรมัน

@ โรมัน: ฉันจำได้ว่าฉันเคยพบสิ่งนี้ในบางโครงการ (ฉันจะเพิ่มโครงการที่ค่อนข้างสะอาด แต่ไม่ใช่ของฉัน) แต่ฉันไม่รู้ว่ามันสะอาดจริงหรือไม่ ฉันได้เพิ่มตัวอย่างเล็ก ๆ ที่ดูเหมือนสิ่งที่ฉันเคยเห็น แต่อีกครั้ง: นี่ไม่ใช่รหัสของฉันและฉันไม่ได้ใช้โครงสร้างนั้นดังนั้นฉันจึงไม่ใช่คนที่มีคุณสมบัติเหมาะสมที่สุดที่จะสร้างตัวอย่างที่ถูกต้อง :) IIRC คลาสภายในถูกตั้งชื่อเสมอเช่นStateCheckerและการโทรมักจะมีลักษณะดังนี้A.StateChecker.check (a)หรืออะไรทำนองนั้น
SyntaxT3rr0r

8
หากคุณบอกว่า“ ไม่มีสิ่งที่เรียกว่า“ คลาสภายในแบบคงที่ ” คำตอบของคุณที่ว่า“ คุณสามารถสร้างทั้งคลาสซ้อนหรือคลาสภายในภายในอินเทอร์เฟซ Java” นั้นผิดโดยพื้นฐาน การใช้คำจำกัดความที่แคบลงinterfaces ไม่สามารถมีคลาสภายในได้ คุณสามารถละเว้นโมดิstaticฟายเออร์ของinterfaceคลาสที่ซ้อนกันได้ แต่ยังคงเป็นคลาสที่ซ้อนกันไม่ใช่คลาสภายใน
Holger

6
คำตอบนี้ผิด อินเทอร์เฟซสามารถมีคลาสซ้อนแบบคงที่ แต่ไม่ใช่คลาสภายใน
Paul Boddington

1
@PaulBoddington คุณพูดถูก แม้กระทั่งการลบ 'static' Bคลาสนี้เป็นคลาสที่ซ้อนกันแบบคงที่ไม่ใช่คลาสภายใน อินเทอร์เฟซได้รับการดูแลเป็นพิเศษ ฉันไม่พบการพูดถึงเรื่องนี้ทางออนไลน์ยกเว้นในข้อมูลจำเพาะ: "คลาสสมาชิกของอินเทอร์เฟซเป็นแบบคงที่โดยปริยายดังนั้นจึงไม่ถือว่าเป็นคลาสภายใน" docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3
Max Barraclough

111

ใช่เราสามารถมีคลาสภายในอินเทอร์เฟซได้ ตัวอย่างหนึ่งของการใช้งานอาจเป็นได้

public interface Input
{
    public static class KeyEvent {
         public static final int KEY_DOWN = 0;
         public static final int KEY_UP = 1;
         public int type;
         public int keyCode;
         public char keyChar;
    }
    public static class TouchEvent {
         public static final int TOUCH_DOWN = 0;
         public static final int TOUCH_UP = 1;
         public static final int TOUCH_DRAGGED = 2;
         public int type;
         public int x, y;
         public int pointer;
    }
    public boolean isKeyPressed(int keyCode);
    public boolean isTouchDown(int pointer);
    public int getTouchX(int pointer);
    public int getTouchY(int pointer);
    public float getAccelX();
    public float getAccelY();
    public float getAccelZ();
    public List<KeyEvent> getKeyEvents();
    public List<TouchEvent> getTouchEvents();
}

ที่นี่โค้ดมีคลาสซ้อนกันสองคลาสซึ่งมีไว้สำหรับการห่อหุ้มข้อมูลเกี่ยวกับอ็อบเจ็กต์เหตุการณ์ซึ่งใช้ในภายหลังในนิยามเมธอดเช่น getKeyEvents () การมีไว้ในอินเทอร์เฟซอินพุตช่วยเพิ่มการทำงานร่วมกัน


3
@ Levit แค่สงสัยว่าคลาสที่ใช้งานจะมีลักษณะอย่างไร?
เปลี่ยนแปลงมากเกินไป

1
ชอบที่จะเห็นการใช้งานจริงสำหรับการใช้งานข้างต้น ขอขอบคุณ.
Prakash K

47

การใช้งานที่ถูกต้อง IMHO กำลังกำหนดอ็อบเจ็กต์ที่ได้รับหรือส่งคืนโดยวิธีการเชื่อมต่อที่แนบมา โครงสร้างการจัดเก็บข้อมูลตามหลักการ ด้วยวิธีนี้หากใช้ออบเจ็กต์สำหรับอินเทอร์เฟซนั้นเท่านั้นคุณจะมีสิ่งต่างๆที่สอดคล้องกันมากขึ้น

ตามตัวอย่าง:

interface UserChecker {
   Ticket validateUser(Credentials credentials);

   class Credentials {
      // user and password
   }

   class Ticket {
      // some obscure implementation
   }
}

แต่อย่างไรก็ตาม ... มันเป็นเรื่องของรสนิยมเท่านั้น


36

อ้างจากข้อมูลจำเพาะ Java 7 :

อินเทอร์เฟซอาจมีการประกาศประเภทสมาชิก (§8.5)

การประกาศประเภทสมาชิกในอินเทอร์เฟซเป็นแบบคงที่และเป็นสาธารณะโดยปริยาย ได้รับอนุญาตให้ระบุตัวดัดแปลงเหล่านี้หรือทั้งสองอย่างซ้ำซ้อน

เป็นไปไม่ได้ที่จะประกาศคลาสที่ไม่คงที่ภายในอินเทอร์เฟซ Java ซึ่งเหมาะสมกับฉัน


ขอขอบคุณ. นี่อาจเป็นคำตอบที่กระชับที่สุดของทั้งหมด
โจเซฟ

1
นี่คือคำตอบที่ฉันกำลังมองหา .. แต่ OP ถามคำถามหลายคำถาม
Charlie Wallace

11

กรณีการใช้งานที่น่าสนใจคือการจัดเรียงการใช้งานเริ่มต้นกับวิธีการอินเทอร์เฟซผ่านคลาสภายในตามที่อธิบายไว้ที่นี่: https://stackoverflow.com/a/3442218/454667 (เพื่อเอาชนะปัญหาการสืบทอดคลาสเดียว)


และนี่คือเหตุผลว่าทำไมคลาสสมาชิกส่วนตัวจึงมีเหตุผล
Vincent

8

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

interface MyInterface {

   public static class MyInterfaceException extends Exception {
   }

   void doSomething() throws MyInterfaceException;
}

7

ใช่มันเป็นไปได้ที่จะมีนิยามคลาสแบบคงที่ภายในอินเทอร์เฟซ แต่สิ่งที่เป็นประโยชน์ที่สุดของฟีเจอร์นี้ก็คือเมื่อใช้ประเภท enum (ซึ่งเป็นคลาสสแตติกชนิดพิเศษ) ตัวอย่างเช่นคุณสามารถมีสิ่งนี้:

public interface User {
    public enum Role {
        ADMIN("administrator"),
        EDITOR("editor"),
        VANILLA("regular user");

        private String description;

        private Role(String description) {
            this.description = description;
        }

        public String getDescription() {
            return description;
        }
    }

    public String getName();
    public void setName(String name);
    public Role getRole();
    public void setRole(Role role);
    ...
}


1

บางทีเมื่อคุณต้องการโครงสร้างที่ซับซ้อนมากขึ้นเช่นพฤติกรรมการใช้งานที่แตกต่างกันให้พิจารณา:

public interface A {
    public void foo();

    public static class B implements A {
        @Override
        public void foo() {
            System.out.println("B foo");
        }
    }
}

นี่คืออินเทอร์เฟซของคุณและนี่คือเครื่องมือที่ใช้:

public class C implements A {
    @Override
    public void foo() {
        A.B b = new A.B();
        b.foo(); 
    }

    public static void main(String[] strings) {
        C c = new C();
        c.foo();
    }
}

อาจมีการใช้งานแบบคงที่ แต่จะไม่ทำให้สับสนฉันไม่รู้


0

ฉันพบการใช้เฟอร์ประเภทนี้

  1. คุณสามารถใช้โครงสร้างนี้เพื่อกำหนดและจัดกลุ่มค่าคงที่สุดท้ายแบบคงที่ทั้งหมด
  2. เนื่องจากเป็นอินเทอร์เฟซที่คุณสามารถใช้กับคลาสได้

คุณสามารถเข้าถึงค่าคงที่ทั้งหมดที่จัดกลุ่มไว้ ชื่อของคลาสทำหน้าที่เป็นเนมสเปซในกรณีนี้


0

คุณยังสามารถสร้างคลาสสแตติก "Helper" สำหรับฟังก์ชันการทำงานทั่วไปสำหรับอ็อบเจ็กต์ที่ใช้อินเทอร์เฟซนี้:

public interface A {
    static class Helper {
        public static void commonlyUsedMethod( A a ) {
           ...
        }
    }
}

0

ฉันต้องการมันตอนนี้ ฉันมีอินเทอร์เฟซที่สะดวกในการคืนคลาสเฉพาะจากหลาย ๆ วิธี คลาสนี้เป็นเพียงคอนเทนเนอร์สำหรับการตอบสนองจากวิธีการของอินเทอร์เฟซนี้

ดังนั้นจึงเป็นการสะดวกที่จะมีนิยามคลาสซ้อนแบบคงที่ซึ่งเชื่อมโยงกับอินเทอร์เฟซนี้เท่านั้นเนื่องจากอินเทอร์เฟซนี้ควรเป็นที่เดียวที่สร้างคลาสคอนเทนเนอร์ผลลัพธ์นี้


0

สำหรับลักษณะอินสแตนซ์(smth เช่นอินเทอร์เฟซที่มีวิธีการใช้งาน) ใน Groovy คอมไพล์ไปยังอินเทอร์เฟซที่มีคลาสภายในซึ่งใช้วิธีการทั้งหมด

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