Java: การประกาศคลาสหลายรายการในไฟล์เดียว


237

ใน Java คุณสามารถกำหนดคลาสระดับบนสุดได้หลายไฟล์ในไฟล์เดียวโดยระบุว่าหนึ่งในนั้นเป็นแบบสาธารณะ (ดูJLS § 7.6 ) ดูตัวอย่างด้านล่าง

  1. มีชื่อเป็นระเบียบเรียบร้อยสำหรับเทคนิคนี้ (คล้ายกับinner, nested, anonymous)?

  2. JLS กล่าวว่าระบบอาจบังคับใช้ข้อ จำกัด ที่คลาสรองเหล่านี้ไม่สามารถทำได้referred to by code in other compilation units of the packageเช่นไม่สามารถใช้เป็นแพคเกจส่วนตัวได้ นั่นเป็นสิ่งที่เปลี่ยนแปลงระหว่างการใช้งานจาวาหรือไม่?

เช่น PublicClass.java:

package com.example.multiple;

public class PublicClass {
    PrivateImpl impl = new PrivateImpl();
}

class PrivateImpl {
    int implementationData;
}

11
+1 คำถามที่ดี ฉันไม่เคยคิดถึงเรื่องที่คิดมากเพราะมันไม่จำเป็นที่จะต้องทำสิ่งนี้
Michael Myers

12
โปรดทราบว่านี่เป็นคุณลักษณะที่ร่องรอย มันจะไม่เป็นไปได้ถ้า java มีคลาสที่ซ้อนตั้งแต่ต้น
Kevin Bourrillion

คำตอบ:


120

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

สำหรับเรื่องนี้การเปลี่ยนแปลงระหว่างการใช้งานจริงหรือไม่ - ฉันสงสัยอย่างมาก แต่ถ้าคุณหลีกเลี่ยงการทำสิ่งแรกคุณจะไม่ต้องสนใจ :)


71
ฉันไม่ใช่ downvoter แต่ความจริงแล้วคำตอบนี้เป็นสิ่งที่เรียกว่า "normative" (เช่น "คุณควร" แทนที่จะเป็น "ในความเป็นจริง ... แต่ ... ") เป็นเหตุผลที่น่าจะเป็นไปได้มากที่สุด รับ downvote ฉันคิดว่า ไม่ตอบคำถามใด ๆ เลย ชอบสร้างข้อยกเว้นที่ไม่เกี่ยวข้องแทนการส่งคืนสิ่งใด / ยกข้อยกเว้นที่มีข้อมูลเกี่ยวกับข้อเท็จจริงจริงแทนที่จะเป็นความคิดเห็น
n611x007

6
ฉันพบสิ่งที่ฉันคิดว่าเป็นข้อยกเว้นเล็กน้อยสำหรับคำแนะนำของ @JonSkeet ในการใช้ประเภทซ้อนกัน (ซึ่งฉันเห็นด้วยอย่างอื่น): หากคลาสหลักทั่วไปและพารามิเตอร์ประเภทคือคลาสที่สองคลาสที่สองไม่สามารถ ซ้อนกัน และถ้าทั้งสองคลาสมีการเชื่อมโยงกันอย่างแน่นหนา (เช่น PublicClass และ PrivateImpl ในคำถาม) ฉันคิดว่ามันเป็นความคิดที่ดีที่จะวาง PrivateImpl เป็นคลาสระดับบนสุดในไฟล์เดียวกัน
jfritz42

6
@BoomerRogers: ไม่นี่ไม่ใช่ "หลักพื้นฐานของการเขียนโปรแกรมตามองค์ประกอบ" หากคุณกำลังเขียนโปรแกรมกับส่วนประกอบทำไมคุณถึงสนใจวิธีการจัดระเบียบซอร์สโค้ด? (โดยส่วนตัวแล้วฉันชอบการฉีดแบบพึ่งพามากกว่ารูปแบบตัวระบุตำแหน่งบริการ แต่นั่นเป็นเรื่องที่แตกต่างกัน) API ที่แยกต่างหากและการจัดระเบียบซอร์สโค้ดในใจของคุณ - เป็นสิ่งที่แตกต่างกันมาก
Jon Skeet

1
@ JonSkeet ให้ฉันใช้ถ้อยคำใหม่: "คำตอบ" ของคุณเป็นความคิดเห็นที่ไม่เกี่ยวข้องส่วนบุคคล (เช่นคำตอบเช่น "เลอะ" และ "ฉันสงสัยว่า" มีค่าน้อย) ดังนั้นโพสต์ของคุณไม่ตอบคำถามใด ๆ ที่โพสต์ทั้งสอง ตรวจสอบคำตอบของ polygenelubricants แล้วคุณจะเห็นว่าเขาจัดการเพื่อตอบทั้งคู่
bvdb

1
@bvdb (และมีจำนวนมาก . สิ่งที่ปฏิบัติไม่ดี แต่ได้รับอนุญาตจากสเปคที่ผมอยากขอร้องให้คนที่จะไม่เขียนpublic int[] foo(int x)[] { return new int[5][5]; }เป็นอย่างดีแม้ว่าที่ถูกต้อง.)
จอนสกีต

130

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

สมมติว่าคุณมีสองไฟล์คือ Foo.java และ Bar.java

Foo.java ประกอบด้วย:

  • ฟูสาธารณะชน

Bar.java ประกอบด้วย:

  • บาร์สาธารณะ
  • คลาส Baz

สมมติว่าคลาสทั้งหมดอยู่ในแพ็คเกจเดียวกัน (และไฟล์อยู่ในไดเรกทอรีเดียวกัน)

จะเกิดอะไรขึ้นถ้า Foo.java อ้างถึง Baz แต่ไม่ใช่ Bar และเราพยายามรวบรวม Foo.java? การรวบรวมล้มเหลวโดยมีข้อผิดพลาดเช่นนี้:

Foo.java:2: cannot find symbol
symbol  : class Baz
location: class Foo
  private Baz baz;
          ^
1 error

มันสมเหตุสมผลถ้าคุณคิดถึงมัน หาก Foo.java อ้างถึง Baz แต่ไม่มี Baz.java (หรือ Baz.class) javac จะรู้ได้อย่างไรว่าไฟล์ต้นฉบับอะไรให้ค้นหาบ้าง

หากคุณบอกให้ javac รวบรวม Foo.java และ Bar.java ในเวลาเดียวกันหรือแม้ว่าคุณจะรวบรวม Bar.java ไว้ก่อนหน้านี้ (ทิ้ง Baz.class ที่ javac สามารถหาได้) ข้อผิดพลาดนี้จะหายไป สิ่งนี้ทำให้กระบวนการสร้างของคุณรู้สึกไม่น่าเชื่อถือและไม่สม่ำเสมอ

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

บางครั้งก็มีเหตุผลที่ดีว่าทำไมทุกคนทำบางสิ่งในลักษณะที่เฉพาะเจาะจง


Maven ทำสิ่งใดเพื่อให้การรวบรวมมีความน่าเชื่อถือหรือไม่
Aleksandr Dubinsky

23

ผมเชื่อว่าคุณก็เรียกว่ามันคืออะไรกPrivateImpl non-public top-level classคุณสามารถประกาศnon-public top-level interfacesเช่นกัน

เช่นที่อื่น ๆ บน SO: คลาสที่ไม่ใช่แบบสาธารณะระดับบนสุดเทียบกับคลาสที่ซ้อนแบบคงที่

สำหรับการเปลี่ยนแปลงพฤติกรรมระหว่างเวอร์ชันมีการสนทนาเกี่ยวกับสิ่งที่ "ทำงานได้อย่างสมบูรณ์แบบ" ใน 1.2.2 แต่หยุดทำงานใน 1.4 ในฟอรั่มของดวงอาทิตย์: Java Compiler - ไม่สามารถที่จะประกาศผู้ที่ไม่ใช่การเรียนระดับบนสุดของประชาชนในแฟ้ม


1
ปัญหาเดียวของฉันคือคุณสามารถเป็นnon-public top level classคลาสเดียวในไฟล์ได้ดังนั้นจึงไม่ได้อยู่หลายหลาก
Michael Brewer-Davis

ฉันเข้าใจความกังวล แต่อย่างที่คุณเห็นว่านี่เป็นคำศัพท์ที่คนอื่น ๆ เคยใช้ในอดีต secondary top level typesถ้าผมมีที่จะทำขึ้นในระยะของตัวเองผมอาจจะเรียกมันว่า
polygenelubricants

7

คุณสามารถมีคลาสได้มากเท่าที่คุณต้องการเช่นนี้

public class Fun {
    Fun() {
        System.out.println("Fun constructor");
    }
    void fun() {
        System.out.println("Fun mathod");
    }
    public static void main(String[] args) {
        Fun fu = new Fun();
        fu.fun();
        Fen fe = new Fen();
        fe.fen();
        Fin fi = new Fin();
        fi.fin();
        Fon fo = new Fon();
        fo.fon();
        Fan fa = new Fan();
        fa.fan();
        fa.run();
    }
}

class Fen {
    Fen() {
        System.out.println("fen construuctor");

    }
    void fen() {
        System.out.println("Fen method");
    }
}

class Fin {
    void fin() {
        System.out.println("Fin method");
    }
}

class Fon {
    void fon() {
        System.out.println("Fon method");
    } 
}

class Fan {
    void fan() {
        System.out.println("Fan method");
    }
    public void run() {
        System.out.println("run");
    }
}

1
@Nenotlep เมื่อคุณทำการ "ปรับปรุงการจัดรูปแบบ" โปรดระวังว่ามันจะไม่ยุ่งกับโค้ดเช่นการลบแบ็กสแลช
Tom

3
ไม่ตอบคำถาม
ᴠɪɴᴄᴇɴᴛ

4

1. มีชื่อที่เป็นระเบียบเรียบร้อยสำหรับเทคนิคนี้ (คล้ายกับภายใน, ซ้อนกัน, ไม่ระบุชื่อ)?

การสาธิตไฟล์เดี่ยวหลายระดับ

2.The JLS กล่าวว่าระบบอาจบังคับใช้ข้อ จำกัด ที่ไม่สามารถอ้างถึงคลาสรองเหล่านี้ในรหัสการรวบรวมหน่วยอื่น ๆ ของแพคเกจเช่นพวกเขาไม่สามารถถือว่าเป็นแพคเกจส่วนตัว นั่นเป็นสิ่งที่เปลี่ยนแปลงระหว่างการใช้งานจาวาหรือไม่?

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


1

อ้างอิงจาก Effective Java 2nd edition (รายการ 13):

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

คลาสที่ซ้อนกันอาจเป็นแบบสแตติกหรือไม่คงที่โดยขึ้นอยู่กับว่าคลาสสมาชิกต้องการเข้าถึงอินสแตนซ์ที่ปิดล้อม (รายการที่ 22)


OP ไม่ได้ถามเกี่ยวกับคลาสที่ซ้อนกัน
charmoniumQ

0

ใช่คุณสามารถกับสมาชิกสาธารณะคงที่ในระดับสาธารณะด้านนอกเช่น:

public class Foo {

    public static class FooChild extends Z {
        String foo;
    }

    public static class ZeeChild extends Z {

    }

}

และไฟล์อื่นที่อ้างถึงข้างต้น:

public class Bar {

    public static void main(String[] args){

        Foo.FooChild f = new Foo.FooChild();
        System.out.println(f);

    }
}

วางไว้ในโฟลเดอร์เดียวกัน รวบรวมกับ:

javac folder/*.java

และทำงานด้วย:

 java -cp folder Bar

ตัวอย่างนั้นไม่ตอบคำถาม คุณกำลังให้ตัวอย่างของคลาสแบบสแตติกที่ซ้อนกันซึ่งไม่เหมือนกับการมีคลาสระดับบนสุดที่กำหนดไว้ในไฟล์เดียวกัน
Pedro García Medina

0

แค่ FYI หากคุณใช้ Java 11+ จะมีข้อยกเว้นสำหรับกฎนี้: หากคุณเรียกใช้ไฟล์ java ของคุณโดยตรง ( โดยไม่ต้องรวบรวม ) ในโหมดนี้ไม่มีข้อ จำกัด สำหรับคลาสสาธารณะเดี่ยวต่อไฟล์ อย่างไรก็ตามคลาสที่มีmainเมธอดต้องเป็นคลาสแรกในไฟล์


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