หลายๆ คนอาจไม่คุ้นเคยกับคลาสภายในของ Java อันที่จริง มีแนวคิดที่คล้ายกันใน C++ นั่นคือคลาสที่ซ้อนกัน จะมีการเปรียบเทียบความแตกต่างและการเชื่อมต่อระหว่างทั้งสองคลาสด้านล่าง โดยผิวเผิน คลาสภายในเป็นเพียงคลาสอื่นที่กำหนดไว้ภายในคลาส (ดังที่คุณเห็นด้านล่าง คลาสภายในสามารถกำหนดได้ในหลาย ๆ ที่) แต่ในความเป็นจริงแล้ว มันไม่ง่ายอย่างนั้น เมื่อมองแวบแรก คลาสภายในอาจดูเหมือนเล็กน้อย ซ้ำซ้อน ประโยชน์อาจไม่ชัดเจนนักสำหรับผู้เริ่มต้น แต่ด้วยความเข้าใจที่ลึกซึ้งยิ่งขึ้น คุณจะพบว่านักออกแบบของ Java มีความตั้งใจที่ดีในคลาสภายใน การเรียนรู้การใช้คลาสภายในเป็นส่วนหนึ่งของการเรียนรู้การเขียนโปรแกรม Java ขั้นสูง ซึ่งช่วยให้คุณออกแบบโครงสร้างโปรแกรมได้สวยงามยิ่งขึ้น มาแนะนำจากประเด็นต่อไปนี้:
การประชุมครั้งแรก
คัดลอกรหัสรหัส ดังต่อไปนี้:
เนื้อหาอินเทอร์เฟซสาธารณะ {
ค่า int();
-
ปลายทางอินเทอร์เฟซสาธารณะ {
สตริง readLabel();
-
สินค้าระดับสาธารณะ {
เนื้อหาคลาสส่วนตัวใช้เนื้อหา {
ส่วนตัว int i = 11;
ค่า int สาธารณะ () {
ส่งคืนฉัน;
-
-
คลาสที่ได้รับการป้องกัน GDestination ใช้ Destination {
ป้ายกำกับสตริงส่วนตัว
GDestination ส่วนตัว (สตริงอยู่ที่ไหน) {
ฉลาก = ที่ไหน;
-
สตริงสาธารณะ readLabel() {
ฉลากส่งคืน;
-
-
ปลายทางสาธารณะ (สตริง) {
ส่งคืน GDestination ใหม่
-
เนื้อหาสาธารณะต่อ () {
กลับเนื้อหาใหม่ ();
-
-
คลาส TestGoods {
โมฆะสาธารณะคงหลัก (สตริง [] args) {
สินค้า p = สินค้าใหม่();
สารบัญ c = p.cont();
ปลายทาง d = p.dest("ปักกิ่ง");
-
-
ในตัวอย่างนี้ เนื้อหาคลาสและ GDestination ถูกกำหนดไว้ภายในคลาส Goods และมีตัวแก้ไขที่ได้รับการป้องกันและส่วนตัวตามลำดับเพื่อควบคุมระดับการเข้าถึง เนื้อหาแสดงถึงเนื้อหาของสินค้า และ Gestination แสดงถึงปลายทางของสินค้า พวกเขาใช้สองอินเทอร์เฟซเนื้อหาและปลายทางตามลำดับ ในวิธีการหลักต่อไปนี้ คุณใช้ Contents c และ Destination d โดยตรงเพื่อดำเนินการ คุณไม่เห็นชื่อของคลาสภายในทั้งสองนี้ด้วยซ้ำ! ด้วยวิธีนี้ ประโยชน์ประการแรกของคลาสภายในคือการซ่อนการดำเนินการที่คุณไม่ต้องการให้ผู้อื่นรู้ นั่นก็คือ การห่อหุ้ม
ในเวลาเดียวกัน เรายังค้นพบวิธีแรกในการรับวัตถุคลาสภายในนอกขอบเขตของคลาสภายนอก ซึ่งก็คือการสร้างและส่งกลับมันโดยใช้วิธีการของคลาสภายนอก วิธี cont() และ dest() ในตัวอย่างข้างต้นทำเช่นนี้ แล้วมีวิธีอื่นอีกไหม?
แน่นอนว่ามี
รูปแบบไวยากรณ์ดังนี้:
outerObject=outerClass ใหม่ (พารามิเตอร์ตัวสร้าง);
outerClass.innerClass innerObject=outerObject.new InnerClass (พารามิเตอร์ตัวสร้าง);
โปรดทราบว่าเมื่อสร้างออบเจ็กต์คลาสภายในที่ไม่คงที่ คุณต้องสร้างออบเจ็กต์คลาสภายนอกที่เกี่ยวข้องก่อน ด้วยเหตุผลนี้ สิ่งนี้นำไปสู่หัวข้อถัดไปของเรา อ็อบเจ็กต์คลาสภายในที่ไม่คงที่มีการอ้างอิงถึงอ็อบเจ็กต์คลาสภายนอก
เล็กน้อย
คัดลอกรหัสรหัส ดังต่อไปนี้:
สินค้าระดับสาธารณะ {
มูลค่า int ส่วนตัว = 2;
เนื้อหาคลาสส่วนตัวใช้เนื้อหา {
ส่วนตัว int i = 11 * valueRate;
ค่า int สาธารณะ () {
ส่งคืนฉัน;
-
-
คลาสที่ได้รับการป้องกัน GDestination ใช้ Destination {
ป้ายกำกับสตริงส่วนตัว
GDestination ส่วนตัว (สตริงอยู่ที่ไหน) {
ฉลาก = ที่ไหน;
-
สตริงสาธารณะ readLabel() {
ฉลากส่งคืน;
-
-
ปลายทางสาธารณะ (สตริง) {
ส่งคืน GDestination ใหม่
-
เนื้อหาสาธารณะต่อ () {
กลับเนื้อหาใหม่ ();
-
-
ที่นี่เราเพิ่มตัวแปรสมาชิกส่วนตัว valueRate ให้กับคลาส Goods ซึ่งหมายถึงค่าสัมประสิทธิ์มูลค่าของสินค้า ซึ่งจะถูกคูณด้วยค่านั้นเมื่อเมธอด value() ของคลาสภายใน Content คำนวณค่า เราพบว่า value() สามารถเข้าถึง valueRate ซึ่งเป็นข้อดีประการที่สองของคลาสภายใน วัตถุคลาสภายในสามารถเข้าถึงเนื้อหาของวัตถุคลาสภายนอกที่สร้างขึ้น แม้แต่ตัวแปรส่วนตัว! นี่เป็นคุณสมบัติที่มีประโยชน์มากที่ให้แนวคิดและทางลัดเพิ่มเติมแก่เราเมื่อออกแบบ เพื่อให้บรรลุฟังก์ชันนี้ วัตถุคลาสภายในจะต้องมีการอ้างอิงไปยังวัตถุคลาสภายนอก เมื่อคอมไพลเลอร์ Java สร้างอ็อบเจ็กต์คลาสภายใน มันจะส่งผ่านการอ้างอิงไปยังอ็อบเจ็กต์คลาสภายนอกโดยปริยายและเก็บไว้ สิ่งนี้ทำให้อ็อบเจ็กต์คลาสภายในสามารถเข้าถึงอ็อบเจ็กต์คลาสภายนอกได้ตลอดเวลา และนี่คือเหตุผลว่าทำไมจึงต้องสร้างออบเจ็กต์คลาสภายในนอกขอบเขตของคลาสภายนอก คุณต้องสร้างออบเจ็กต์คลาสภายนอกก่อน
บางคนอาจถามว่า จะทำอย่างไรถ้าตัวแปร Member ในคลาส Inner มีชื่อเดียวกับตัวแปร Member ในคลาสภายนอก นั่นคือตัวแปร Member ของคลาสภายนอกที่มีชื่อเดียวกันถูกบล็อก? ไม่เป็นไร Java ใช้รูปแบบต่อไปนี้เพื่อแสดงการอ้างอิงถึงคลาสภายนอก:
ด้านนอกClass.this
ด้วยสิ่งนี้ เราไม่กลัวสถานการณ์การป้องกันนี้
คลาสภายในแบบคงที่
เช่นเดียวกับคลาสทั่วไป คลาสภายในสามารถเป็นแบบคงที่ได้ อย่างไรก็ตาม เมื่อเปรียบเทียบกับคลาสภายในที่ไม่คงที่ ความแตกต่างก็คือคลาสภายในแบบคงที่ไม่มีการอ้างอิงภายนอก จริงๆ แล้วสิ่งนี้คล้ายกันมากกับคลาสที่ซ้อนกันใน C++ ความแตกต่างที่ใหญ่ที่สุดระหว่างคลาสภายในของ Java และคลาสที่ซ้อนกันของ C++ คือมีการอ้างอิงถึงภายนอกหรือไม่ แน่นอนว่าจากมุมมองของการออกแบบและรายละเอียดบางส่วน
นอกจากนี้ ในคลาสภายในที่ไม่คงที่ใดๆ ไม่สามารถมีข้อมูลคงที่ วิธีการคงที่ หรือคลาสภายในคงที่อื่นๆ ได้ (คลาสภายในสามารถซ้อนกันได้มากกว่าหนึ่งระดับ) แต่คุณสามารถมีทั้งหมดนี้ได้ในคลาสภายในแบบคงที่ นี่ถือได้ว่าเป็นข้อแตกต่างที่สองระหว่างทั้งสอง
ชั้นเรียนชั้นในในท้องถิ่น
ใช่ คลาสภายในของ Java อาจเป็นแบบโลคัลก็ได้ โดยสามารถกำหนดภายในเมธอดหรือแม้แต่บล็อกโค้ดได้
คัดลอกรหัสรหัส ดังต่อไปนี้:
สินค้าระดับสาธารณะ1 {
ปลายทางสาธารณะ (สตริง) {
คลาส GDestination ใช้ Destination {
ป้ายกำกับสตริงส่วนตัว
GDestination ส่วนตัว (สตริงอยู่ที่ไหน) {
ฉลาก = ที่ไหน;
-
สตริงสาธารณะ readLabel() {
ฉลากส่งคืน;
-
-
ส่งคืน GDestination ใหม่
-
โมฆะสาธารณะคงหลัก (สตริง [] args) {
สินค้า1 g = สินค้าใหม่1();
ปลายทาง d = g.dest("ปักกิ่ง");
-
-
ข้างต้นเป็นตัวอย่างหนึ่งดังกล่าว ใน method deest เรากำหนดคลาสภายใน และสุดท้ายเมธอดนี้จะส่งคืนอ็อบเจ็กต์ของคลาสภายในนี้ หากเราต้องการเพียงสร้าง object ของคลาสภายในและสร้างมันออกมาภายนอก เราก็สามารถทำได้ แน่นอนว่าคลาสภายในที่กำหนดในวิธีการสามารถกระจายการออกแบบได้ และการใช้งานไม่ได้จำกัดอยู่เพียงเท่านี้
นี่เป็นตัวอย่างที่แปลกยิ่งกว่านี้:
คัดลอกรหัสรหัส ดังต่อไปนี้:
สินค้าระดับสาธารณะ2 {
โมฆะส่วนตัว InternalTracking (บูลีน b) {
ถ้า (ข) {
TrackingSlip คลาส {
รหัสสตริงส่วนตัว;
TrackingSlip (สตริง) {
ไอดี = ส;
-
สตริง getSlip() {
รหัสส่งคืน;
-
-
TrackingSlip ts = TrackingSlip ใหม่ ("สลิป");
สตริง s = ts.getSlip();
-
-
โมฆะสาธารณะติดตาม () {
การติดตามภายใน (จริง);
-
โมฆะสาธารณะคงหลัก (สตริง [] args) {
สินค้า2 g = สินค้าใหม่2();
g.ติดตาม();
-
-
คุณไม่สามารถสร้างอ็อบเจ็กต์ของคลาสภายในนี้ภายนอก if ได้ เพราะมันอยู่นอกเหนือขอบเขตของมัน อย่างไรก็ตาม ในระหว่างการคอมไพล์ TrackingSlip คลาสภายในจะถูกคอมไพล์ในเวลาเดียวกันกับคลาสอื่น ยกเว้นว่ามีขอบเขตของตัวเองและไม่ถูกต้องเกินกว่าขอบเขตนี้ นอกเหนือจากนั้น ก็ไม่แตกต่างจากคลาสภายในอื่น ๆ
ชนชั้นภายในที่ไม่ระบุชื่อ
กฎไวยากรณ์ของคลาสภายในที่ไม่ระบุตัวตนของ Java อาจดูแปลกเล็กน้อย แต่ก็เหมือนกับอาร์เรย์ที่ไม่ระบุชื่อ เมื่อคุณต้องการเพียงสร้างอ็อบเจ็กต์ของคลาสและไม่ต้องการชื่อ การใช้คลาสภายในสามารถทำให้โค้ดดูกระชับและชัดเจน กฎไวยากรณ์มีดังนี้:
ชื่ออินเทอร์เฟซใหม่(){......}; หรือชื่อซุปเปอร์คลาสใหม่(){......};
มาดูตัวอย่างด้านล่างกันต่อ:
คัดลอกรหัสรหัส ดังต่อไปนี้:
สินค้าระดับสาธารณะ3 {
เนื้อหาสาธารณะต่อ () {
คืนเนื้อหาใหม่ () {
ส่วนตัว int i = 11;
ค่า int สาธารณะ () {
ส่งคืนฉัน;
-
-
-
-
วิธีการ cont() ในที่นี้ใช้คลาสภายในที่ไม่ระบุชื่อเพื่อส่งคืนออบเจ็กต์ของคลาสที่ใช้อินเทอร์เฟซ Contents โดยตรง ซึ่งดูเรียบง่ายมากจริงๆ
ในอะแด็ปเตอร์ที่ไม่ระบุชื่อสำหรับการประมวลผลเหตุการณ์ Java คลาสภายในที่ไม่ระบุชื่อถูกใช้อย่างกว้างขวาง ตัวอย่างเช่น เมื่อต้องการปิดหน้าต่าง ให้เพิ่มโค้ดนี้:
คัดลอกรหัสรหัส ดังต่อไปนี้:
frame.addWindowListener (WindowAdapter ใหม่ () {
โมฆะสาธารณะ windowClosing (WindowEvent e) {
System.ออก(0);
-
-
สิ่งหนึ่งที่ควรทราบก็คือ เนื่องจากคลาสภายในที่ไม่ระบุชื่อไม่มีชื่อ จึงไม่มีตัวสร้าง (แต่ถ้าคลาสภายในที่ไม่ระบุชื่อสืบทอดคลาสพาเรนต์ที่มีเฉพาะตัวสร้างแบบพารามิเตอร์เท่านั้น มันจะต้องนำพารามิเตอร์เหล่านี้มาเมื่อสร้างมัน และใช้ซุปเปอร์ คีย์เวิร์ดเพื่อเรียกเนื้อหาที่เกี่ยวข้องในระหว่างกระบวนการใช้งาน) หากคุณต้องการเริ่มต้นตัวแปรสมาชิก มีหลายวิธี:
หากอยู่ในคลาสภายในที่ไม่ระบุตัวตนของเมธอด คุณสามารถใช้เมธอดนี้เพื่อส่งพารามิเตอร์ที่คุณต้องการได้ แต่จำไว้ว่าพารามิเตอร์เหล่านี้จะต้องได้รับการประกาศขั้นสุดท้าย
แปลงคลาสภายในที่ไม่ระบุชื่อเป็นคลาสภายในท้องถิ่นที่มีชื่อเพื่อให้สามารถมีตัวสร้างได้
ใช้บล็อกการเริ่มต้นในคลาสภายในที่ไม่ระบุชื่อนี้
ทำไมคุณถึงต้องการชั้นเรียนภายใน?
ประโยชน์ของคลาสภายในของ Java คืออะไร? ทำไมคุณถึงต้องการชั้นเรียนภายใน?
ก่อนอื่น เรามายกตัวอย่างง่ายๆ กันก่อน หากคุณต้องการใช้อินเทอร์เฟซ แต่เมธอดในอินเทอร์เฟซนี้มีชื่อและพารามิเตอร์เหมือนกับเมธอดในคลาสที่คุณคิดขึ้นมา คุณควรทำอย่างไร? ในเวลานี้ คุณสามารถสร้างคลาสภายในเพื่อใช้อินเทอร์เฟซนี้ได้ เนื่องจากคลาสภายในสามารถเข้าถึงได้โดยทุกสิ่งในคลาสภายนอก การทำเช่นนั้นจะบรรลุฟังก์ชันการทำงานทั้งหมดที่คุณมีหากคุณใช้อินเทอร์เฟซโดยตรง
แต่คุณอาจจะอยากตั้งคำถามว่าแค่เปลี่ยนวิธีการยังไม่พอหรือ?
แท้จริงแล้วการใช้สิ่งนี้เป็นเหตุผลในการออกแบบคลาสภายในนั้นไม่น่าเชื่อจริงๆ
เหตุผลที่แท้จริงก็คือ คลาสภายในและอินเทอร์เฟซใน Java ร่วมกันสามารถแก้ปัญหาที่โปรแกรมเมอร์ C++ มักบ่นใน Java โดยไม่มีการสืบทอดหลายรายการ ในความเป็นจริง การสืบทอดหลายรายการของ C++ นั้นซับซ้อนมากในการออกแบบ และ Java สามารถบรรลุผลของการสืบทอดหลายรายการได้เป็นอย่างดีผ่านคลาสภายในและอินเทอร์เฟซ