คลาสภายในที่ไม่ระบุชื่อและตัวอย่าง - 1

“สวัสดี อามีโก้!”

"แต่เราได้กล่าวสวัสดีแล้ว Ellie!"

“เฮ้ อย่าเถียงกับป้าของคุณ ในศตวรรษที่ 31 ถ้าคุณไม่ได้เจอใครนานกว่าครึ่งชั่วโมง มันเป็นธรรมเนียมที่จะต้องทักทายอีกครั้ง ดังนั้นอย่าบอกทัศนคติของคุณกับฉัน!”

"อย่างไรก็ตาม ถึงเวลาสำหรับหัวข้อที่น่าสนใจอื่น: การสืบพันธุ์ของหุ่นยนต์!"

"O_O"

"ล้อเล่น หัวข้อใหม่คือคลาสภายในนิรนาม "

"ใน Java บางครั้งมีบางสถานการณ์ที่คุณต้องใช้คลาสเพื่อสืบทอดหลายคลาส เนื่องจาก Java ไม่รองรับการสืบทอดหลายรายการ พวกเขาได้แก้ปัญหานี้โดยใช้คลาสภายใน: ในคลาสของเรา เราประกาศคลาสภายในและสร้าง มันสืบทอดคลาสที่เราต้องการให้สืบทอด นี่คือตัวอย่าง:"

ตัวอย่างของคลาสภายในที่สืบทอดคลาสเธรด
class Tiger extends Cat
{
 public void tigerRun()
 {
  .....
 }

 public void startTiger()
 {
  TigerThread thread = new TigerThread();
  thread.start();
 }

 class TigerThread extends Thread
 {
  public void run()
  {
   tigerRun();
  }
 }
}

"ลองมาดูตัวอย่างอื่น:"

เราต้องการคลาสย่อยของคลาสเธรดเพื่อแทนที่เมธอดรันของมัน"

"นั่นคือเหตุผลที่ในคลาส Tiger เราประกาศ คลาสภายใน TigerThreadซึ่งสืบทอด Thread และแทนที่เมธอดการรัน

"เพื่อความสะดวก เราได้กำหนดสองเมธอดในคลาส Tiger: tigerRun และ startTiger (ซึ่งคล้ายกับเมธอด run และ start ของ Thread"

"ในเมธอด tigerStart เราสร้าง ออบเจกต์ TigerThreadและเรียกใช้เมธอด start()"

"JVM จะสร้างเธรดใหม่ที่จะเริ่มทำงานเมื่อมีการเรียกใช้เมธอดของ TigerThread "

"เมธอดนี้เรียก เมธอด run ของเรา : tigerRun "

"ฉันเคยทำงานกับเธรดมาก่อน ดังนั้นสิ่งนี้จึงดูตรงไปตรงมา"

"เราต้องตั้งชื่อเมธอด tigerRun และ tigerStart หรือไม่"

"ไม่ เราอาจเรียกมันว่า run and start แต่ฉันก็อยากแสดงให้เห็นว่าเราไม่ได้สืบทอด Thread คำอธิบายอาจทำให้สับสนมากกว่านี้"

"ตกลง ถ้าอย่างนั้นฉันคิดว่าฉันเข้าใจแล้ว แต่ถ้ามีการเรียก tigerStart เป็นครั้งที่สอง เราจะสร้างและเริ่มวัตถุเธรดที่สอง นั่นไม่ได้หมายความว่าเราจะมี «เสือตัวหนึ่งทำงานบนเธรดที่แตกต่างกันสองเธรด» "

"คุณไม่เฉียบแหลมเหรอ! คุณพูดถูกและนั่นไม่ดี มาเขียนโค้ดใหม่ดังนี้:"

รหัส
class Tiger extends Cat
{
 public void tigerRun()
 {
  .....
 }

 public void startTiger()
 {
  thread.start();
 }

 private TigerThread thread = new TigerThread();

 private class TigerThread extends Thread
 {
  public void run()
  {
   tigerRun();
  }
 }
}

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

"ถูกต้อง ครั้งที่สองที่เสือออกสตาร์ท คุณจะได้รับข้อยกเว้น"

"ฉันจับผิดได้ดีกว่าเธอแล้ว เอลลี่!"

"ใช่ คุณทำได้ดีมาก ถ้าอย่างนั้นเรามาเข้าคลาสภายในที่ไม่ระบุตัวตนกันเถอะ"

"หมายเหตุหลาย ๆ แง่มุมของโค้ดด้านบน:"

1)เราสืบทอดคลาสเธรด แต่ไม่มีการนำโค้ดไปใช้จริง "มันเป็น «เราต้องสืบทอดคลาสเธรด» มากกว่า «เราสืบทอดมาเพื่อขยาย»

2)จะมีการสร้างวัตถุ TigerThread เพียงรายการเดียวเท่านั้น

กล่าวอีกนัยหนึ่ง เราเขียนโค้ดทั้งหมดเพื่อแทนที่เมธอดหนึ่งและสร้างออบเจกต์หนึ่ง

คุณจำได้ไหมว่าฉันพูดถึงการประดิษฐ์คอนสตรัคเตอร์ได้อย่างไร?

ก่อนที่ตัวสร้าง หลังจากตัวสร้าง
TigerThread thread = new TigerThread();

private class TigerThread extends Thread
{
 public void run()
 {
  tigerRun();
 }
}
Thread thread = new Thread()
{
 public void run()
 {
  tigerRun();
 }
};

"ฉันเห็นว่าโค้ดกระชับขึ้น แต่ฉันไม่ค่อยเข้าใจว่าเกิดอะไรขึ้น"

"เราสามารถรวมสี่สิ่งเป็นหนึ่งเดียว:"

1) การประกาศคลาสที่ได้รับ

2) การเอาชนะวิธีการ

3) การประกาศตัวแปร

4) การสร้างอินสแตนซ์ของคลาสที่ได้รับ

"อันที่จริง สิ่งที่เรากำลังทำคือการรวมการดำเนินการสองอย่างเข้าด้วยกัน: การประกาศคลาสที่ได้รับมา และสร้างอินสแตนซ์ของคลาสนั้น"

ไม่มีคลาสนิรนาม ด้วยคลาสที่ไม่ระบุชื่อ
Cat tiger = new Tiger();

class Tiger extends Cat
{
}
Cat tiger = new Cat()
{
};

"มาสำรวจไวยากรณ์กันอีกครั้ง:"

การประกาศตัวแปรเธรด
Thread thread = new Thread();
การประกาศตัวแปรประเภท «คลาสนิรนามที่สืบทอดเธรด»
Thread thread = new Thread()
{

};

"โปรดทราบว่าเราไม่ได้แค่กำหนดคลาสใหม่เท่านั้น เรากำลังสร้างตัวแปร—มีเครื่องหมายอัฒภาคต่อท้าย!"

"และหากเราต้องการแทนที่เมธอดการรัน เราต้องเขียนสิ่งนี้:"

การประกาศตัวแปรเธรด
Thread thread = new Thread()
{
 public void run()
  {
   System.out.println("new run-method");
  }
};

“คุณตามไปเร็ว ทำได้ดีมาก!”

"ขอบคุณ ถ้าเราต้องการเมธอดอื่นที่ไม่ได้เป็นส่วนหนึ่งของคลาสเธรดล่ะ"

"คุณสามารถเขียนได้"

"แม้ว่าจะไม่เปิดเผยตัวตน แต่นี่คือชนชั้นภายในที่เต็มเปี่ยม:"

รหัสจาวา คำอธิบาย
Thread thread = new Thread()
{
  public void run()
  {
   printHi();
  }

  public void printHi()
  {
   System.out.println("Hi!");
  }
};	 
สีแดง: รหัสสำหรับสร้างตัวแปร

สีเขียว: รหัสสำหรับสร้างวัตถุ

สีน้ำเงิน: รหัสสำหรับคลาสที่ได้มาแบบไม่ระบุชื่อ

"ชั้นในเต็มเปี่ยม?"

"ฉันสามารถใช้ตัวแปรของชั้นนอกได้หรือไม่"

"อย่างแน่นอน."

"และฉันสามารถส่งบางอย่างให้กับตัวสร้างได้หรือไม่"

"ใช่ แต่เฉพาะอาร์กิวเมนต์สำหรับคอนสตรัคเตอร์ของซูเปอร์คลาสเท่านั้น:"

ระดับ ตัวอย่างของคลาสภายในที่ไม่ระบุชื่อ
class Cat
{
 int x, y;
 Cat(int x, int y)
 {
  this.x = x;
  thix.y = y;
 }
}
Cat cat = new Cat(3,4)
{
  public void print()
  {
   System.out.println(x+" "+y);
  }
};

"เราไม่สามารถเพิ่มพารามิเตอร์ของเราเองให้กับคอนสตรัคเตอร์ของคนอื่นได้ แต่เราสามารถใช้ตัวแปรของคลาสภายนอกได้ ซึ่งจะชดเชยข้อบกพร่องนี้ได้อย่างดี"

"จะเกิดอะไรขึ้นถ้าฉันยังต้องการเพิ่มพารามิเตอร์อื่นๆ ให้กับตัวสร้าง"

"จากนั้นประกาศคลาสภายในธรรมดา (ไม่ระบุชื่อ) และใช้สิ่งนั้น"

“ใช่ ฉันเกือบลืมเรื่องนั้นไปแล้ว”

"จะเกิดอะไรขึ้นถ้าฉันประกาศตัวแปรสแตติก นั่นจะทำให้คลาสนิรนามกลายเป็นคลาสซ้อนแบบสแตติกแทนที่จะเป็นคลาสภายในหรือไม่ กล่าวอีกนัยหนึ่ง มันจะไม่มีการอ้างอิงถึงคลาสภายนอก"

"ไม่ มันจะเป็นชนชั้นในนิรนาม ดูตัวอย่างเหล่านี้"

ด้วยคลาสที่ไม่ระบุชื่อ ไม่มีคลาสนิรนาม
Thread thread = new Thread()
{
  public void run()
  {
   tigerRun();
  }
};
TigerThread thread = new TigerThread();

private class TigerThread extends Thread
{
 public void run()
 {
  tigerRun();
 }
}
static Thread thread = new Thread()
{
  public void run()
  {
   tigerRun();
  }
};
static TigerThread thread = new TigerThread();

private class TigerThread extends Thread
{
 public void run()
 {
  tigerRun();
 }
}

"ฉันเข้าใจแล้ว ตัวแปรสแตติกเท่านั้นที่จะเป็นแบบสแตติก ไม่ใช่คลาส"

"ใช่."

"ในความเป็นจริง คอมไพเลอร์สร้างคลาสภายในสำหรับคลาสภายในที่ไม่ระบุชื่อทั้งหมด คลาสเหล่านี้มักมีชื่อว่า «1», «2», «3» และอื่นๆ"