class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
คลาสภายในเหล่านี้เรียกว่าซ้อนกัน แบ่งออกเป็น 2 ประเภทคือ
- คลาสที่ซ้อนกันแบบไม่คงที่ สิ่งเหล่านี้เรียกว่าคลาสภายใน
- คลาสที่ซ้อนกันแบบคงที่
- ชั้นเรียนในท้องถิ่น
- คลาสที่ไม่ระบุตัวตน

public class Bicycle {
private String model;
private int weight;
public Bicycle(String model, int weight) {
this.model = model;
this.weight = weight;
}
public void start() {
System.out.println("Let's go!");
}
public class Handlebar {
public void right() {
System.out.println("Steer right!");
}
public void left() {
System.out.println("Steer left!");
}
}
public class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
}
ที่นี่เรามีBicycle
ชั้นเรียน มันมี 2 ฟิลด์และ 1 วิธีการ: start()
. 
Handlebar
และ Seat
รหัสของพวกเขาถูกเขียนขึ้นในBicycle
ชั้นเรียน คลาสเหล่านี้เป็นคลาสเต็มรูปแบบ อย่างที่คุณเห็น แต่ละคลาสมีวิธีการของตัวเอง ณ จุดนี้ คุณอาจมีคำถามว่า ทำไมเราถึงใส่คลาสหนึ่งไว้ในอีกคลาสหนึ่ง ทำไมต้องสร้างชั้นเรียนภายใน? สมมติว่าเราต้องการคลาสแยกต่างหากสำหรับแนวคิดของแฮนด์บาร์และเบาะนั่งในโปรแกรมของเรา แน่นอน เราไม่จำเป็นต้องทำให้มันซ้อนกัน! เราสามารถจัดชั้นเรียนธรรมดา ตัวอย่างเช่น:
public class Handlebar {
public void right() {
System.out.println("Steer right!");
}
public void left() {
System.out.println("Steer left");
}
}
public class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
คำถามดีมาก! แน่นอนว่าเราไม่ได้ถูกจำกัดด้วยเทคโนโลยี การทำเช่นนั้นเป็นตัวเลือกอย่างแน่นอน สิ่งสำคัญคือการออกแบบคลาสที่ถูกต้องจากมุมมองของโปรแกรมเฉพาะและวัตถุประสงค์ของมัน คลาสภายในมีไว้สำหรับแยกเอนทิตีที่เชื่อมโยงกับเอนทิตีอื่นอย่างแยกไม่ออก แฮนด์ เบาะ และแป้นเหยียบเป็นส่วนประกอบของจักรยาน แยกจากจักรยาน มันไม่สมเหตุสมผลเลย หากเราแยกแนวคิดเหล่านี้ออกจากคลาสสาธารณะ เราก็จะได้โค้ดแบบนี้ในโปรแกรมของเรา:
public class Main {
public static void main(String[] args) {
Handlebar handlebar = new Handlebar();
handlebar.right();
}
}
อืม... ความหมายของรหัสนี้ยากที่จะอธิบายด้วยซ้ำ เรามีแฮนด์จับที่คลุมเครืออยู่บ้าง (ทำไมจึงจำเป็น? พูดตามตรงไม่มีความคิด) และคันบังคับนี้ก็เลี้ยวขวา... ด้วยตัวเอง โดยไม่มีจักรยาน... ด้วยเหตุผลบางประการ ด้วยการแยกแนวคิดของแฮนด์บาร์ออกจากแนวคิดของจักรยาน เราสูญเสียตรรกะบางอย่างในโปรแกรมของเรา เมื่อใช้คลาสภายใน รหัสจะดูแตกต่างไปจากเดิมมาก:
public class Main {
public static void main(String[] args) {
Bicycle peugeot = new Bicycle("Peugeot", 120);
Bicycle.Handlebar handlebar = peugeot.new Handlebar();
Bicycle.Seat seat = peugeot.new Seat();
seat.up();
peugeot.start();
handlebar.left();
handlebar.right();
}
}
เอาต์พุตคอนโซล:
Seat up!
Let's go!
Steer left!
Steer right!
ตอนนี้สิ่งที่เราเห็นก็มีเหตุผล! :) เราสร้างวัตถุจักรยาน เราสร้าง "subobjects" ของจักรยานสองชิ้น — แฮนด์บาร์และเบาะนั่ง เรายกที่นั่งขึ้นเพื่อความสบายและออกไป: ถีบและบังคับเลี้ยวตามต้องการ! :) เมธอดที่เราต้องการนั้นถูกเรียกบนวัตถุที่เหมาะสม ทุกอย่างง่ายและสะดวก ในตัวอย่างนี้ การแยกแฮนด์บาร์และเบาะนั่งออกช่วยเพิ่มการห่อหุ้ม (เราซ่อนข้อมูลเกี่ยวกับชิ้นส่วนจักรยานไว้ในคลาสที่เกี่ยวข้อง) และช่วยให้เราสร้างนามธรรมที่มีรายละเอียดมากขึ้นได้ ทีนี้มาดูสถานการณ์อื่นกัน สมมติว่าเราต้องการสร้างโปรแกรมจำลองร้านจักรยานและอะไหล่จักรยาน 
-
วัตถุของชั้นในไม่สามารถอยู่ได้โดยไม่มีวัตถุของชั้นนอก
สิ่งนี้สมเหตุสมผล: นี่คือเหตุผลที่เราสร้าง
Seat
และHandlebar
คลาสภายในในโปรแกรมของเรา — เพื่อไม่ให้จบลงด้วยแฮนด์บาร์และเบาะนั่งที่รกร้างรหัสนี้ไม่ได้รวบรวม:
public static void main(String[] args) { Handlebar handlebar = new Handlebar(); }
คุณสมบัติที่สำคัญอีกประการหนึ่งดังต่อไปนี้:
-
วัตถุของชั้นในสามารถเข้าถึงตัวแปรของชั้นนอก
ตัวอย่างเช่น ลองเพิ่ม
int seatPostDiameter
ตัวแปร (แทนเส้นผ่านศูนย์กลางของหลักอาน) ให้กับBicycle
ชั้นเรียน ของเราจากนั้นใน
Seat
คลาสภายใน เราสามารถสร้างdisplaySeatProperties()
เมธอดที่แสดงคุณสมบัติของที่นั่งได้:public class Bicycle { private String model; private int weight; private int seatPostDiameter; public Bicycle(String model, int weight, int seatPostDiameter) { this.model = model; this.weight = weight; this.seatPostDiameter = seatPostDiameter; } public void start() { System.out.println("Let's go!"); } public class Seat { public void up() { System.out.println("Seat up!"); } public void down() { System.out.println("Seat down!"); } public void displaySeatProperties() { System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
และตอนนี้เราสามารถแสดงข้อมูลนี้ในโปรแกรมของเรา:
public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); Bicycle.Seat seat = bicycle.new Seat(); seat.displaySeatProperties(); } }
เอาต์พุตคอนโซล:
Seat properties: seatpost diameter = 40
บันทึก:ตัวแปรใหม่จะถูกประกาศด้วยตัวดัดแปลงการเข้าถึงที่เข้มงวดที่สุด (
private
) แล้วยังคนชั้นในเข้าได้! -
ไม่สามารถสร้างอ็อบเจกต์ของคลาสภายในด้วยวิธีสแตติกของคลาสภายนอก
สิ่งนี้อธิบายได้จากคุณลักษณะเฉพาะของการจัดระเบียบชั้นเรียนภายใน คลาสภายในสามารถมีตัวสร้างพร้อมพารามิเตอร์หรือตัวสร้างเริ่มต้น แต่ไม่ว่าอย่างไรก็ตาม เมื่อเราสร้างวัตถุของคลาสภายใน การอ้างอิงถึงวัตถุของคลาสภายนอกจะถูกส่งผ่านไปยังวัตถุที่สร้างขึ้นของคลาสภายใน ท้ายที่สุดแล้ว การมีวัตถุอ้างอิงดังกล่าวเป็นข้อกำหนดที่สมบูรณ์ มิฉะนั้นเราจะไม่สามารถสร้างวัตถุของชั้นในได้
แต่ถ้าเมธอดของคลาสนอกเป็นแบบสแตติก เราก็อาจไม่มีอ็อบเจกต์ของคลาสนอก! และนี่จะเป็นการละเมิดตรรกะของวิธีการทำงานของชนชั้นใน ในสถานการณ์นี้ คอมไพเลอร์จะสร้างข้อผิดพลาด:
public static Seat createSeat() { // Bicycle.this cannot be referenced from a static context return new Seat(); }
-
คลาสภายในไม่สามารถมีตัวแปรและเมธอดคงที่ได้
ตรรกะก็เหมือนกัน: เมธอดและตัวแปรสแตติกสามารถมีอยู่และถูกเรียกใช้หรืออ้างอิงได้แม้ในกรณีที่ไม่มีออบเจกต์
แต่ถ้าไม่มีวัตถุของชนชั้นนอก เราก็จะไม่สามารถเข้าถึงชนชั้นในได้
ย้อนแย้งชัดๆ! ด้วยเหตุนี้จึงไม่อนุญาตให้ใช้ตัวแปรและเมธอดแบบสแตติกในคลาสภายใน
คอมไพลเลอร์จะสร้างข้อผิดพลาดหากคุณพยายามสร้าง:
public class Bicycle { private int weight; public class Seat { // An inner class cannot have static declarations public static void displaySeatProperties() { System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
-
เมื่อสร้างออบเจกต์ของคลาสภายใน ตัวแก้ไขการเข้าถึงนั้นมีความสำคัญ
คลาสภายในสามารถทำเครื่องหมายด้วยตัวดัดแปลงการเข้าถึงมาตรฐาน:
public
,private
,protected
,package private
และทำไมเรื่องนี้?
สิ่งนี้ส่งผลต่อตำแหน่งที่เราสามารถสร้างอินสแตนซ์ของคลาสภายในในโปรแกรมของเรา
ถ้า
Seat
คลาสของเราถูกประกาศเป็นpublic
, เราสามารถสร้างSeat
อ็อบเจกต์ในคลาสอื่นก็ได้ ข้อกำหนดเพียงอย่างเดียวคือต้องมีวัตถุของคลาสภายนอกด้วยอย่างไรก็ตาม เราได้ทำสิ่งนี้แล้วที่นี่:
public class Main { public static void main(String[] args) { Bicycle peugeot = new Bicycle("Peugeot", 120); Bicycle.Handlebar handlebar = peugeot.new Handlebar(); Bicycle.Seat seat = peugeot.new Seat(); seat.up(); peugeot.start(); handlebar.left(); handlebar.right(); } }
เราเข้าถึง
Handlebar
ชั้นในได้อย่างง่ายดายจากMain
ชั้นเรียนถ้าเราประกาศคลาสภายในเป็น
private
เราจะสามารถสร้างวัตถุภายในคลาสภายนอกเท่านั้นเราไม่สามารถสร้าง
Seat
วัตถุ "ภายนอก" ได้อีกต่อไป:private class Seat { // Methods } public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); // Bicycle.Seat has private access in Bicycle Bicycle.Seat seat = bicycle.new Seat(); } }
คุณอาจเข้าใจตรรกะแล้ว :)
-
ตัวแก้ไขการเข้าถึงสำหรับคลาสภายในทำงานเหมือนกับตัวแปรทั่วไป
ตัว
protected
แก้ไขจัดเตรียมการเข้าถึงตัวแปรอินสแตนซ์ในคลาสย่อยและคลาสที่อยู่ในแพ็คเกจเดียวกันprotected
ยังใช้ได้กับชั้นเรียนภายใน เราสามารถสร้างprotected
วัตถุของชั้นใน:- ในชั้นนอก;
- ในคลาสย่อยของมัน
- ในคลาสที่อยู่ในแพ็คเกจเดียวกัน
หากคลาสภายในไม่มีตัวแก้ไขการเข้าถึง (
package private
) สามารถสร้างวัตถุของคลาสภายในได้:- ในชั้นนอก;
- ในคลาสที่อยู่ในแพ็คเกจเดียวกัน
คุณคุ้นเคยกับตัวดัดแปลงมาเป็นเวลานาน ดังนั้นจึงไม่มีปัญหา
GO TO FULL VERSION