ด้วยคลาสที่ไม่ระบุชื่อคุณจะประกาศคลาสแบบ "ไม่มีชื่อ" สำหรับคลาสที่ซ้อนกันคอมไพเลอร์จะสร้างคลาสพับลิกแบบสแตนด์อโลนใหม่ที่มีตัวสร้างที่จะใช้ตัวแปรทั้งหมดที่ใช้เป็นอาร์กิวเมนต์ สิ่งนี้เกิดขึ้นเนื่องจากสภาพแวดล้อมรันไทม์ไม่มีความคิดเกี่ยวกับคลาสที่ซ้อนกันดังนั้นจึงจำเป็นต้องมีการแปลง (อัตโนมัติ) จากคลาสที่ซ้อนกันเป็นคลาสแบบสแตนด์อโลน
นำรหัสนี้มาเป็นตัวอย่าง:
public class EnclosingClass {
public void someMethod() {
String shared = "hello";
new Thread() {
public void run() {
// this is not valid, won't compile
System.out.println(shared); // this instance expects shared to point to the reference where the String object "hello" lives in heap
}
}.start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
}
ไม่สามารถใช้งานได้เพราะนี่คือสิ่งที่คอมไพเลอร์ทำงานภายใต้ประทุน:
public void someMethod() {
String shared = "hello";
new EnclosingClass$1(shared).start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
คลาสแบบไม่ระบุชื่อดั้งเดิมจะถูกแทนที่ด้วยคลาสแบบสแตนด์อโลนบางตัวที่คอมไพเลอร์สร้างขึ้น (โค้ดไม่ถูกต้อง แต่ควรให้ความคิดที่ดีแก่คุณ):
public class EnclosingClass$1 extends Thread {
String shared;
public EnclosingClass$1(String shared) {
this.shared = shared;
}
public void run() {
System.out.println(shared);
}
}
อย่างที่คุณเห็นคลาสสแตนด์อโลนเก็บการอ้างอิงไปยังวัตถุที่ใช้ร่วมกันโปรดจำไว้ว่าทุกอย่างใน java คือการส่งต่อค่าดังนั้นแม้ว่าตัวแปรอ้างอิง 'แชร์' ใน EnclosingClass จะได้รับการเปลี่ยนแปลงอินสแตนซ์นั้นชี้ไปที่จะไม่แก้ไข และตัวแปรอ้างอิงอื่น ๆ ทั้งหมดที่ชี้ไป (เช่นในคลาสที่ไม่ระบุชื่อ: การปิดล้อม $ 1) จะไม่รับรู้สิ่งนี้ นี่คือเหตุผลหลักที่คอมไพเลอร์บังคับให้คุณประกาศตัวแปร 'ที่ใช้ร่วมกัน' นี้ถือเป็นที่สุดดังนั้นพฤติกรรมประเภทนี้จะไม่ทำให้เป็นรหัสที่ใช้งานอยู่
ตอนนี้เป็นสิ่งที่เกิดขึ้นเมื่อคุณใช้ตัวแปรอินสแตนซ์ภายในคลาสนิรนาม (นี่คือสิ่งที่คุณควรทำเพื่อแก้ปัญหาของคุณย้ายตรรกะของคุณไปที่เมธอด "อินสแตนซ์" หรือตัวสร้างคลาส):
public class EnclosingClass {
String shared = "hello";
public void someMethod() {
new Thread() {
public void run() {
System.out.println(shared); // this is perfectly valid
}
}.start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
}
คอมไพล์นี้ดีเพราะคอมไพเลอร์จะแก้ไขโค้ดเพื่อให้คลาสที่สร้างขึ้นใหม่การปิดล้อม $ 1 จะเก็บการอ้างอิงไปยังอินสแตนซ์ของ EnclosingClass ที่มันถูกสร้างอินสแตนซ์ (นี่เป็นเพียงการแสดง แต่ควรให้คุณไป):
public void someMethod() {
new EnclosingClass$1(this).start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
public class EnclosingClass$1 extends Thread {
EnclosingClass enclosing;
public EnclosingClass$1(EnclosingClass enclosing) {
this.enclosing = enclosing;
}
public void run() {
System.out.println(enclosing.shared);
}
}
เช่นนี้เมื่อตัวแปรอ้างอิง 'แบ่งใช้' ใน EnclosingClass ได้รับการกำหนดใหม่และสิ่งนี้เกิดขึ้นก่อนที่จะเรียกการเรียกใช้เธรด # () คุณจะเห็น "อื่น ๆ สวัสดี" พิมพ์สองครั้งเพราะตอนนี้ตัวแปร EnclosingClass $ 1 # จะเก็บการอ้างอิง ไปยังวัตถุของคลาสที่มีการประกาศดังนั้นการเปลี่ยนแปลงคุณลักษณะใด ๆ บนวัตถุนั้นจะสามารถมองเห็นได้โดยอินสแตนซ์ของ EnclosingClass $ 1
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับหัวข้อนี้คุณสามารถดูโพสต์บล็อกยอดเยี่ยม (ไม่ได้เขียนโดยฉัน): http://kevinboone.net/java_inner.html