Tin tức mới

Gặp gỡ bạn Lớp Trong (Inner Class)

Nguồn

Người dịch: Nguyễn Minh Tân | Biên tập: Phạm Anh Đới

Trong chuyên mục đặc biệt của “Những câu truyện bên bếp lửa” lần này, chúng ta sẽ được nghe một Đối tượng (Object) chia sẻ nhưng suy tư thầm kín của mình.

Làm một đối tượng như tôi không hề vui như mọi người nghĩ.

Cuộc sống dần trở nên cô độc…

Ngoài này…

trên đống(heap) …

một mình.

Không kể đến sự khủng hoảng, cảm xúc tuyệt vọng khi biết rằng tham chiếu cuối cùng của mình đang tuột đi mất và, bạn nhận ra – bạn vừa trở thành miếng mồi cho tên thu gom rác (garbage collector).

Nhưng, bạn biết điều gì giúp tôi vượt qua không? Một lớp trong. Nàng có thể xoa dịu sự cô độc, v.v. chừng nào mà có ai đó tạo ra một thực thể của nàng. Tất cả những gì tôi mong muốn là có một ai đó để gằn kết với.


Một ai đó để chia sẻ những suy tư thầm kín nhất (và các biến, các phương thức). Một ai đó biết MỌI THỨ về mình. Một mối quan hệ thân mật giữa hai đối tượng – một trong và một ngoài.

Tôi bảo vệ rất kĩ càng lớp trong của mình. Nếu có ai đó muốn tạo ra một thực thể của nàng, hắn phải bước qua tôi – một đối tượng lớp ngoài.

Lớp trong của tôi không thể tồn tại độc lập được. Tôi, với tư cách của một thực thể lớp ngoài, có thể sống một mình (tuy rằng không hạnh phúc lắm). Bạn không cần phải tạo một thực thể lớp trong để có một thực thể lớp ngoài. Nhưng bạn sẽ KHÔNG BAO GIỜ tạo được một thực thể lớp trong nếu không có một đối tượng lớp ngoài để “gắn” nó vào.

Lớp trong của tôi cần có tôi.

Chúng tôi có một sự gắn kết đặc biệt đó.

Việc đó làm cho cuốc sống trên đống mà có thể bị thu gom rác ngoài này có thể chấp nhận được.


Dưới đây là một cách nhỏ để mai mối hai đối tượng:

class Outer
{
private int size ;
private String thoughts = “My outer thoughts”;

    class Inner
{
String innerThoughts = “My inner thoughts”;

void doStuff()
{
// Đối tượng trong có riêng “this” 
System.out.println( innerThoughts );

// và nó cũng có một loại “outer this”
// cho những dữ liệu riêng tư của lớp ngoài

System.out.println( thoughts );
}
}
}

Nhưng, sẽ không có chuyện gì xẩy ra cho đến khi có ai đó tạo ra một thực thể cho cả hai lớp.

class TestMe
{

    public static void main( String args[] )
{
// Tạo một thể hiện của tôi, đối tượng của lớp ngoài

Outer o = new Outer();

// Inner i = new Inner();
// KHÔNG! Không thể tự tạo một thể hiện của lớp trong

Outer.Inner i = o.new Inner();
// bây giờ tôi có một đối tượng đặc biệt

i.doStuff();
// Gọi được các phương thức của lớp trong

}

}

Bạn cũng có thể tạo ra thể hiện cho cả hai lớp cùng một lúc:

    Inner i = new Outer().new Inner();

Tôi biết việc trên trông không bình thường lắm, nhưng nó cho ta thấy cần phải có một đối tượng ngoài để có thể tạo ra một đối tượng trong. Trong ví dụ trên, bạn thậm chí còn không có một tham chiếu đến đối tượng ngoài… mà chỉ có tham chiếu của đối tượng trong – “i”.

Tôi ghét static!

Chắc bạn đã từng nghe đến cụm từ lớp trong tĩnh (static inner class). Hừm, chúng không đáng được gọi là lớp trong!

Một lớp trong tĩnh trông như sau:

class Outer
{
static class Inner
{
}

Tôi không thích chúng vì chúng không cho tôi sự gắn kết đặc biết giữa đối tượng và đối tượng ấy. Trong thực tế, lớp trong tĩnh còn không được gọi là lớp trong. Kĩ thuật mà nói, chúng là các lớp lồng nhau (top-level nested).

Một lớp tĩnh lồng nhau có thể được thực thể hóa, nhưng đối tượng tạo ra không chia sẻ bất kì mối quan hệ đặc biệt nào với lớp ngoài.

Lớp static nested chỉ gắn kết với lớp ngoài, chứ không phải một thể hiện của lớp ngoài.

     Outer.Inner i = new Outer.Inner(); 

Đó là lí do vì sao bạn có thể tạo ra một thực thể của lớp static nested mà không cần một thực thể lớp ngoài, cũng như cách bạn gọi một phương thức static của một lớp mà không cần đến một thực thể của lớp đó.

Nhưng, chúng ta hãy quay lại với các lớp trong; chúng có nhiều ý nghĩa hơn. Và bạn có biết rằng tôi thậm chí có thể gắn kết với một thực thể lớp trong mà thậm chí không cần biết TÊN của nàng? Thật tiện lợi, bạn có thể định nghĩa một lớp trong và tạo thực thể của nó cùng một lúc.

Cách làm như sau…

Hãy tưởng tượng bạn – một lập trình viên đang tạo ra một đồ họa giao diện người dùng (GUI) xinh xắn. Bạn quyết định rằng bạn cần biết khi nào thì người dùng sẽ ấn vào nút GO. “Tôi nhận ra mình cần phải có một đối tượng bắt sự kiện tên là ActionListener”, bạn tự nói với chính mình. Vậy là bạn bắt đầu gõ:

   goButton.addActionListener([object goes here]);

Sau đó bạn vỗ trán và nhận ra… “mình không thể tạo ra một thực thể… mình thậm chí còn không có một lớp ActionListener!”

Bạn chưa từng tạo ra một lớp có thể cài đặt giao diện ActionListener.

Không vấn đề.

Bạn có thể tạo ra một lớp mới cài đặt giao diện ActionListener, VÀ tạo ra thực thể của lớp đó – Tất cả trong tham số của phương thức addActionListener của đối tượng của Button.

Có tuyệt vời không?

Nó trông như thể này:

goButton.addActionListener
(
new ActionListener()
{
public void actionPerformed( ActionEvent e )
{
doImportantStuff();
}
}
)

Và hoạt động như thế này:

new ActionListener()

nói với trình biên dich rằng: “Hãy tạo ra một thực thể của một lớp mới, nặc danh cài đặt giao diện ActionListener…”

Sau cái mở ngoặc đó (đánh dấu mầu xanh ở trên), bạn định nghĩa một lớp vô danh…

public void actionPerformed( actionEvent e )
{
doImportantStuff();
}

Phương thức actionPerformed giống hệt như bất kì phương thức nào khác mà bạn bắt buộc phải định nghĩa trong một lớp đi cài đặt giao diện ActionListener. Nhưng lớp mới này không có tên. Đó là vì sao nó được gọi là lớp trong nặc danh.

Và hãy để ý, bạn không nói “new MyActionClass()”. Bạn nói “new ActionListener()”. Nhưng không phải bạn đang tạo ra một thực thể của ActionListener, bạn đang tạo ra thực thể của một lớp nặc danh mới có cài đặt giao diện ActionListener.

“Đợi đã!”, bạn hét lên, “Nếu tôi không muốn cài đặt một giao diện thì sao… nếu tôi muốn tạo ra một lớp trong nặc danh kế thừa từ một lớp khác?”

Một lần nữa, không vấn đề.

Bất cứ thứ gì bạn viết sau từ “new” như “new Something()”, nếu Something là một giao diện thì lớp nặc danh sẽ cài đặt giao diện đó (và phải định nghĩa mọi phương thức của giao diện đó). Nhưng nếu Something là một lớp thì, lớp nặc danh của bạn sẽ tự động trở thành lớp kế thừa của lớp đó. Thật hoàn hảo cho các lớp chuyển đổi sự kiện (event adapter) như WindowAdapter.

Cuối cùng, đừng quên đóng tham số của

goButton.addActionListener(

bằng cách kết thúc ngoặc tròn cùng với dấu chấm phẩy.

     );

Lập trình viên thường bỏ quên cái dấu chấm phẩy tí teo đó ở dưới, vì câu lệnh được bắt đầu từ tít tắp đâu đó ở trên.

Nguy hiểm nguy hiểm!

Bây giờ tôi cảm thấy bắt buộc phải cảnh báo bạn về một vấn đề với lớp trong nặc danh hay bất kì lớp trong nào được định nghĩa trong một phương thức (ngược lại với trong lớp nhưng không trong phương thức). Lớp trong không thể dùng biến cục bộ từ phương thức mà nó được định nghĩa!

Cuối cùng thì, khi kết thúc phương thức, các biến cục bộ sẽ bị thổi bay. Bụp. Biến mất. Quá khứ. Đối tượng trong mà bạn tạo ra từ lớp trong có thể vẫn còn sống và đang quẫy đạp để thoát ra ngoài vùng “heap” rất lâu sau khi các biến cục bộ đã biến mất khỏi tầm ngắm.

Tuy nhiên, bạn vẫn có thể sử dụng các biến cục bộ được định nghĩa với tù khóa final, vì trình biên dịch đã xử lí việc đó cho bạn từ trước. Nhưng chỉ vậy mà thôi – không tham số nào của phương thức đó và không biến cục bộ nào cả.

Một cảnh báo khác về các lớp trong, chúng không thể khai báo bất kì thành phần static nào, trừ phi đó là các hằng thời gian biên dịch (compile-time) và là các biến nguyên thủy hoặc chuỗi (String). (Điều này đương nhiên không đúng với các lớp nested static). Nhưng bạn cũng không phải lo, trình biên dịch sẽ ngăn bạn lại nếu thử.

Cuối cùng, Bạn cũng cần biết rằng một số lập trình viên rất ghét các lớp trong, đặc biệt là các lớp nặc danh. Một số người còn nhận định rằng chúng không “hướng đối tượng” lắm. Một số khác thì xù lông lên vì tính bao gói bị vi phạm, bởi, đối tượng trong có thể truy cập đến dữ liệu riêng tư (private) của đối tượng ngoài.

Nhưng bạn biết tôi nói gì không?

Chính vì thế!

Vì cái mối quan hệ đặc biệt đó, sự gắn kết thân mật đó mà lớp trong trở nên thực tế. Nếu không bạn sẽ phải tạo ra phương thức khởi tạo cho các lớp trong của mình, sau đó lại truyền vào các tham chiếu tới các biến,v.v. Khi bạn tạo thực thể của một lớp trong… bạn phải làm tất cả những gì cần làm với một lớp không-trong cũ kĩ và, đối xử với nó như một kẻ ngoại lai.

Đến lúc bạn cân nhắc xem liệu lớp trong có phù hợp với mình không, làm ơn…

hãy nghĩ đến tôi…

một kẻ đáng thương, cô độc, đối tượng…

nằm trên đống “heap”…

một mình.

Tôi biết bạn sẽ làm điều đúng đắn.

(À, đừng tự bắt mình dừng lại chỉ với một lớp trong và một đối tượng trong… không có chỗ nào trong Java nói rằng đối tượng ngoài phải đơn thê cả)

Kết.

Trích lời David Lu, một độc giả tận tuy:

Cái lớp ngoài đó đang có một mối quan hệ thật bỉ ổi. Đầu tiên, mọi thứ trông thật đẹp đẽ và lãng mạn, có một lớp trong “đặc biệt” để cùng chia sẻ. Nhưng sau đó tôi nhận ra rằng hắn có thể ngoại tình cùng với nhiều lớp trong khác.

Sau đó, tên lớp ngoài bắt đầu có các mối quan hệ “nặc danh”! Hắn còn không thèm biết tên của họ! Điều gì đang xẩy ra ở đây vậy? Tình một đêm? ****? Tình yêu tự do? Tôi cảm thấy shock!

Lớp ngoài không có bình luận gì thêm.

Nguồn

Người dịch: Nguyễn Minh Tân | Biên tập: Phạm Anh Đới


Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.

2 thoughts on “Gặp gỡ bạn Lớp Trong (Inner Class)

  1. bạn cho mình hỏi bạn dịch ra từ nguồn nào vậy? mình thấy cách tiếp cận vấn đề này rất bổ ích, có phải là head first k vậy?

Leave a Reply

Your email address will not be published. Required fields are marked *

TÀI LIỆU DEVWORLD
Cẩm nang phát triển nghề nghiệp cho lập trình viên!