Các lớp bên trong ẩn danh và các ví dụ - 1

"Chào, Amigo!"

"Nhưng chúng ta đã nói xin chào rồi, Ellie!"

"Này, đừng cùng cô cô tranh cãi, ở thế kỷ 31, hơn nửa giờ không gặp, nên chào hỏi theo thói quen, cho nên cũng đừng có thái độ như vậy!"

"Dù sao thì, đã đến lúc cho một chủ đề thú vị khác: sinh sản robot!"

"O_O."

"Đùa thôi, chủ đề mới là các lớp ẩn danh bên trong ."

"Trong Java, đôi khi có những tình huống mà bạn sẽ cần một lớp để kế thừa nhiều lớp. Vì Java không hỗ trợ đa kế thừa, nên họ đã giải quyết vấn đề này bằng cách sử dụng các lớp bên trong: trong lớp của chúng tôi, chúng tôi khai báo một lớp bên trong và thực hiện nó kế thừa bất kỳ lớp nào chúng ta cần nó kế thừa. Đây là một ví dụ:"

Ví dụ về lớp bên trong kế thừa lớp Thread
class Tiger extends Cat
{
 public void tigerRun()
 {
  .....
 }

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

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

"Hãy đào sâu vào một ví dụ khác:"

Chúng ta cần một lớp con của lớp Thread để ghi đè phương thức chạy của nó."

"Đó là lý do tại sao trong lớp Tiger, chúng tôi đã khai báo lớp bên trong TigerThread , lớp kế thừa Thread và ghi đè phương thức chạy.

"Để thuận tiện, chúng tôi đã định nghĩa hai phương thức trong lớp Tiger: tigerRun và startTiger (tương tự như các phương thức chạy và bắt đầu của Thread."

"Trong phương thức tigerStart, chúng tôi tạo một đối tượng TigerThread và gọi phương thức start() của nó."

"JVM sẽ tạo một luồng mới sẽ bắt đầu chạy khi phương thức chạy của TigerThread được gọi."

"Phương thức này sau đó gọi phương thức chạy của chúng ta : tigerRun ."

"Tôi đã làm việc với các chủ đề trước đây, vì vậy điều này có vẻ đơn giản."

"Chúng ta có phải đặt tên cho các phương thức là tigerRun và tigerStart không?"

"Không, chúng tôi có thể gọi chúng là chạy và bắt đầu, nhưng tôi cũng muốn chứng minh rằng chúng tôi không kế thừa Thread. Một lời giải thích có thể khó hiểu hơn."

"OK. Vậy thì tôi nghĩ là tôi hiểu rồi. Nhưng nếu TigerStart được gọi lần thứ hai, chúng ta sẽ tạo và bắt đầu một đối tượng Thread thứ hai. Điều đó không có nghĩa là chúng ta sẽ có «một con hổ chạy trên hai luồng khác nhau» sao? "

"Chà, bạn không nhạy bén sao! Bạn nói đúng, và điều đó không tốt. Hãy viết lại mã như thế này:"

Mã số
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();
  }
 }
}

"Nó không hoàn toàn hoàn hảo. Bạn vẫn không thể gọi một phương thức như vậy hai lần. Nhưng lần này, ít nhất chúng ta sẽ không tạo một luồng thứ hai và làm cho mọi thứ có vẻ ổn."

"Đúng vậy. Lần thứ hai bắt đầu Tiger, bạn sẽ có một ngoại lệ."

"Tôi đã phát hiện ra những sai lầm tốt hơn bạn, Ellie!"

"Yeah, bạn đang làm rất tốt. Vậy thì hãy chuyển sang các lớp học ẩn danh bên trong."

"Lưu ý một số khía cạnh của đoạn mã trên:"

1) Chúng tôi đã kế thừa lớp Thread, nhưng thực tế không triển khai mã nào ở đó. "Đó là «chúng tôi phải kế thừa lớp Thread» hơn là «chúng tôi kế thừa nó để mở rộng nó».

2) Chỉ có một đối tượng TigerThread được tạo.

Nói cách khác, chúng tôi đã viết cả đống mã chỉ để ghi đè lên một phương thức và tạo một đối tượng.

Bạn có nhớ tôi đã nói về việc phát minh ra các nhà xây dựng như thế nào không?

Trước các nhà xây dựng Sau khi xây dựng
TigerThread thread = new TigerThread();

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

"Tôi thấy mã đã trở nên nhỏ gọn hơn, nhưng tôi không hiểu chuyện gì đang xảy ra."

"Chúng ta có thể kết hợp bốn điều thành một:"

1) khai báo lớp dẫn xuất

2) ghi đè phương thức

3) khai báo một biến

4) tạo một thể hiện của lớp dẫn xuất.

"Thực tế, những gì chúng ta đang làm là kết hợp hai thao tác: khai báo một lớp dẫn xuất và tạo một thể hiện của lớp đó."

Không có lớp ẩn danh Với lớp ẩn danh
Cat tiger = new Tiger();

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

"Hãy khám phá lại cú pháp:"

Khai báo một biến Thread
Thread thread = new Thread();
Khai báo một biến có kiểu là «một lớp ẩn danh kế thừa Chủ đề»
Thread thread = new Thread()
{

};

"Lưu ý rằng chúng ta không chỉ định nghĩa một lớp mới. Chúng ta đang tạo một biến—có dấu chấm phẩy ở cuối!"

"Và nếu chúng ta muốn ghi đè phương thức chạy, thì chúng ta cần viết điều này:"

Khai báo một biến Thread
Thread thread = new Thread()
{
 public void run()
  {
   System.out.println("new run-method");
  }
};

"Bạn bắt kịp nhanh. Làm tốt lắm!"

"Cảm ơn. Điều gì sẽ xảy ra nếu chúng ta cần các phương thức khác không thuộc lớp Thread?"

"Bạn có thể viết chúng."

"Mặc dù ẩn danh, đây là một lớp bên trong chính thức:"

mã Java Sự miêu tả
Thread thread = new Thread()
{
  public void run()
  {
   printHi();
  }

  public void printHi()
  {
   System.out.println("Hi!");
  }
};
Màu đỏ: mã để tạo biến.

Màu xanh lá cây: mã để tạo đối tượng.

Màu xanh lam: mã cho lớp dẫn xuất ẩn danh.

"Một lớp bên trong chính thức?"

"Vì vậy, tôi có thể sử dụng các biến của lớp bên ngoài?"

"Tuyệt đối."

"Và tôi có thể chuyển thứ gì đó cho hàm tạo?"

"Có, nhưng chỉ các đối số cho hàm tạo của siêu lớp:"

Lớp học Thể hiện của một lớp bên trong ẩn danh
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);
  }
};

"Chúng ta không thể thêm các tham số của riêng mình vào hàm tạo của người khác. Nhưng chúng ta có thể sử dụng các biến của lớp bên ngoài, điều này bù đắp một cách tuyệt vời cho thiếu sót này."

"Nếu tôi vẫn thực sự cần thêm các tham số khác vào hàm tạo thì sao?"

"Sau đó khai báo một lớp bên trong bình thường (không ẩn danh) và sử dụng nó."

“Phải rồi, suýt chút nữa tôi đã quên mất điều đó.”

"Điều gì sẽ xảy ra nếu tôi khai báo một biến tĩnh? Điều đó có làm cho lớp ẩn danh trở thành lớp lồng tĩnh thay vì lớp bên trong không? Nói cách khác, nó có thiếu tham chiếu đến lớp bên ngoài không?"

"Không. Nó sẽ là một lớp bên trong ẩn danh. Hãy xem những ví dụ này."

Với lớp ẩn danh Không có lớp ẩn danh
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();
 }
}

"Tôi hiểu rồi. Chỉ có biến tĩnh là tĩnh, không phải lớp."

"Chuẩn rồi."

"Trên thực tế, trình biên dịch tạo các lớp bên trong cho tất cả các lớp bên trong ẩn danh. Các lớp này thường được đặt tên là «1», «2», «3», v.v."