Lớp con lớp trong lập trình Java
Một lớp lồng trong là gì?
Như tên của nó đã gợi ý, trong ngôn ngữ lập trình Java một lớp lồng trong là một lớp được khai báo trong một lớp khác. Đây là một ví dụ đơn giản:
public class EnclosingClass {
...
public class ...public class NestedClass {
...
}
}
Thông thường, các lập trình viên giỏi định nghĩa các lớp lồng trong khi lớp lồng trong chỉ có ý nghĩa bên trong bối cảnh của lớp bao bọc bên ngoài. Một số ví dụ phổ biến như sau:
* Các trình xử lý sự kiện trong một lớp UI.
* Các lớp Helper cho các thành phần UI trong một thành phần UI khác.
* Các lớp Adapter để biến đổi bộ phận bên trong của một lớp thành một số dạng khác cho người dùng lớp này.
Bạn có thể định nghĩa một lớp lồng trong là lớp công khai (public), riêng tư (private) hay có bảo vệ (protected). Bạn cũng có thể định nghĩa một lớp lồng trong là lớp final (để ngăn cho nó không bị thay đổi), lớp trừu tượng (abstract) (có nghĩa là nó không thể khởi tạo thành cá thể cụ thể) hoặc lớp tĩnh (static).
Khi bạn tạo ra một lớp static bên trong một lớp khác, bạn đang tạo ra cái được gọi một cách phù hợp nhất là lớp lồng trong. Một lớp lồng trong được định nghĩa bên trong một lớp khác, nhưng có thể tồn tại bên ngoài một cá thể của lớp bao ngoài. Nếu lớp lồng trong của bạn không phải là lớp static, nó chỉ có thể tồn tại bên trong một cá thể của lớp bao ngoài và được gọi một cách phù hợp hơn là lớp bên trong (inner class). Nói khác đi, mọi lớp bên trong là lớp lồng trong nhưng không phải mọi lớp lồng trong là lớp bên trong. Phần lớn các lớp lồng trong mà bạn sẽ gặp phải trong sự nghiệp của bạn sẽ là lớp bên trong hơn là các lớp chỉ đơn giản lồng trong.
Bất kỳ lớp lồng trong nào đều có quyền truy cập vào tất cả các thành viên của lớp bao ngoài, ngay cả khi chúng được khai báo là private.
* Các trình xử lý sự kiện trong một lớp UI.
* Các lớp Helper cho các thành phần UI trong một thành phần UI khác.
* Các lớp Adapter để biến đổi bộ phận bên trong của một lớp thành một số dạng khác cho người dùng lớp này.
Bạn có thể định nghĩa một lớp lồng trong là lớp công khai (public), riêng tư (private) hay có bảo vệ (protected). Bạn cũng có thể định nghĩa một lớp lồng trong là lớp final (để ngăn cho nó không bị thay đổi), lớp trừu tượng (abstract) (có nghĩa là nó không thể khởi tạo thành cá thể cụ thể) hoặc lớp tĩnh (static).
Khi bạn tạo ra một lớp static bên trong một lớp khác, bạn đang tạo ra cái được gọi một cách phù hợp nhất là lớp lồng trong. Một lớp lồng trong được định nghĩa bên trong một lớp khác, nhưng có thể tồn tại bên ngoài một cá thể của lớp bao ngoài. Nếu lớp lồng trong của bạn không phải là lớp static, nó chỉ có thể tồn tại bên trong một cá thể của lớp bao ngoài và được gọi một cách phù hợp hơn là lớp bên trong (inner class). Nói khác đi, mọi lớp bên trong là lớp lồng trong nhưng không phải mọi lớp lồng trong là lớp bên trong. Phần lớn các lớp lồng trong mà bạn sẽ gặp phải trong sự nghiệp của bạn sẽ là lớp bên trong hơn là các lớp chỉ đơn giản lồng trong.
Bất kỳ lớp lồng trong nào đều có quyền truy cập vào tất cả các thành viên của lớp bao ngoài, ngay cả khi chúng được khai báo là private.
Định nghĩa các lớp lồng trong
Bạn định nghĩa một lớp lồng trong đúng như bạn định nghĩa một lớp thông thường khác, nhưng bạn thực hiện nó trong một lớp bao ngoài. Một ví dụ như đã bày sẵn là hãy định nghĩa một lớp Wallet bên trong lớp Adult. Cho dù trong thực tế bạn có thể có một Cái ví (Wallet) tách khỏi một Adult, nhưng điều này sẽ không có ích lắm và điều có ý nghĩa hơn là mọi Adult đều có một Wallet (hoặc ít nhất là một thứ gì đó để giữ tiền, nhưng nếu dùng MoneyContainer nghe hơi lạ). Cũng là có nghĩa khi cho rằng Wallet sẽ không tồn tại trong Person, bởi vì một Baby không có ví và tất cả các lớp con của Person sẽ thừa kế nó nếu nó tồn tại trong Person.
Lớp Wallet của chúng ta sẽ khá đơn giản, vì nó chỉ phục vụ để minh họa định nghĩa về một lớp lồng trong:
protected class Wallet {
protected protected ArrayList bills = new ArrayList();
protected protected void addBill(int aBill) {bills.add(new Integer(aBill));
}
protected } protected int getMoneyTotal() {int total = 0;
for (for (Iterator i = bills.iterator(); i.hasNext(); ) {Integer wrappedBill = (Integer) i.next();int bill = wrappedBill.intValue();total += bill;
}
return }return total;
}
}
Chúng ta sẽ định nghĩa lớp này bên trong Adult, giống như sau:
public class Adult extends Person {
protected protected Wallet wallet = new Wallet();
public public Adult() {
}
public }public void talk() {System.out.println("Spoke.");
}
public }public void acceptMoney(int aBill) {this.wallet.addBill(aBill);
}
public }public int moneyTotal() {
return return this.wallet.getMoneyTotal();
}
protected class }protected class Wallet {
...
}
}
Lưu ý rằng chúng ta đã thêm acceptMoney() để cho phép một Adult nhận thêm tiền. (Xin cứ tự nhiên mở rộng ví dụ để bắt buộc Adult của bạn phải chi tiêu một vài thứ, đó là việc phổ biến trong cuộc sống thực).
Sau khi chúng ta có lớp lồng trong và phương thức acceptMoney() mới, chúng ta có thể sử dụng chúng như sau:
Sau khi chúng ta có lớp lồng trong và phương thức acceptMoney() mới, chúng ta có thể sử dụng chúng như sau:
Adult anAdult = new Adult();anAdult.acceptMoney(5);System.out.println("I have this much money: " + anAdult.moneyTotal());
Thực hiện mã này sẽ cho kết quả rằng anAdult có một tổng số tiền là 5.
Xử lý sự kiện rất đơn giản
Ngôn ngữ Java định nghĩa một cách tiếp cận xử lý sự kiện với các lớp kết hợp để cho phép bạn tạo và xử lý các sự kiện của riêng bạn. Nhưng việc xử lý sự kiện có thể đơn giản hơn nhiều. Tất cả những gì mà bạn thực sự cần là một lô gic nào đó để sinh ra một “sự kiện” (mà thực sự không cần phải hoàn toàn là một lớp sự kiện) và một lô gic nào đó để lắng nghe sự kiện và sau đó trả lời một cách thích hợp. Ví dụ, giả sử rằng bất cứ khi nào một Person di chuyển, hệ thống của chúng ta tạo ra (hoặc kích hoạt) một MoveEvent, mà chúng ta có thể chọn xử lý hay không xử lý. Điều này sẽ yêu cầu một số thay đổi cho hệ thống của chúng ta. Chúng ta phải:
* Tạo ra một lớp “ứng dụng” (application) để khởi chạy hệ thống của chúng ta và minh họa việc sử dụng lớp bên trong vô danh.
* Tạo một MotionListener mà ứng dụng của chúng ta có thể thực hiện và sau đó xử lý các sự kiện trong trình lắng nghe (listener).
* Thêm một List của các trình lắng nghe vào Adult.
* Thêm một phương thức addMotionListener() vào Adult để đăng ký trình lắng nghe.
* Thêm một phương thức fireMoveEvent() vào Adult để nó có thể báo cho trình lắng nghe khi nào thì xử lý sự kiện.
* Thêm mã vào ứng dụng của chúng ta để tạo ra một Adult và tự đăng ký như là một trình xử lý
Tất cả điều này dễ hiểu. Đây là lớp Adult của chúng ta với các thứ mới thêm:
public class Adult extends Person {
protected protected Wallet wallet = new Wallet();
protected protected ArrayList listeners = new ArrayList();
public public Adult() {
}
public }public void move() {super.move(); fireMoveEvent();
}
...
public }...public void addMotionListener(MotionListener aListener) {listeners.add(aListener);
}
protected }protected void fireMoveEvent() {Iterator iterator = listeners.iterator();
while(while(iterator.hasNext()) {MotionListener listener = (MotionListener) iterator.next();listener.handleMove(this);
}
}
protected class }}protected class Wallet {
...
}
}
Lưu ý rằng bây giờ chúng ta ghi đè move(), đầu tiên gọi move() trên Person, sau đó gọi fireMoveEvent() để báo cho trình lắng nghe trả lời. Chúng ta cũng đã thêm phương thức addMotionListener() để thêm một MotionListener vào một danh sách trình lắng nghe đang hoạt động. Đây là những gì giống với một MotionListener:
public interface MotionListener {
public public void handleMove(Adult eventSource);
}
Tất cả những gì còn lại là tạo ra lớp ứng dụng của chúng ta:
Lớp này thực hiện giao diện MotionListener có nghĩa là nó triển khai thực hiện phương thức handleMove(). Tất cả những điều mà chúng ta làm ở đây là in một thông báo để minh họa những gì xảy ra khi một sự kiện được kích hoạt.
public class CommunityApplication implements MotionListener {
public public void handleMove(Adult eventSource) {System.out.println("This Adult moved: n" + eventSource.toString());
}
public static }public static void main(String[] args) {CommunityApplication application = new CommunityApplication();Adult anAdult = new Adult();anAdult.addMotionListener(application);anAdult.move();
}
}
Lớp này thực hiện giao diện MotionListener có nghĩa là nó triển khai thực hiện phương thức handleMove(). Tất cả những điều mà chúng ta làm ở đây là in một thông báo để minh họa những gì xảy ra khi một sự kiện được kích hoạt.
Các lớp bên trong vô danh
Các lớp bên trong vô danh cho phép bạn định nghĩa một lớp ngay tại chỗ, mà không đặt tên nó, để cung cấp một số hành vi trong bối cảnh cụ thể. Đó là một cách tiếp cận phổ biến cho các trình xử lý sự kiện trong các giao diện người dùng, bàn về chúng là một chủ đề vượt ra ngoài phạm vi của hướng dẫn này. Nhưng chúng ta có thể sử dụng một lớp bên trong vô danh ngay cả trong ví dụ xử lý sự kiện rất đơn giản của chúng ta.
Bạn có thể chuyển đổi ví dụ từ các trang trước để sử dụng một lớp bên trong vô danh bằng cách thay đổi lời gọi đến addMotionListener() trong CommunityApplication.main() học lập trình như sau:
anAdult.addMotionListener(new MotionListener() {
public public void handleMove(Adult eventSource) {System.out.println("This Adult moved: n" + eventSource.toString());
}
});
Thay vì có CommunityApplication triển khai thực hiện MotionListener, chúng ta khai báo một lớp bên trong không đặt tên (và như vậy là vô danh) có kiểu MotionListener và đã cung cấp cho nó một triển khai thực hiện handleMove(). Sự việc MotionListener là một giao diện, không phải là một lớp, là không quan trọng. Cả hai đều có thể chấp nhận được.
Mã này sinh ra chính xác cùng một kết quả giống như các phiên bản trước đó, nhưng nó sử dụng một cách tiếp cận phổ biến và đáng mong muốn hơn. Bạn sẽ hầu như luôn luôn thấy các trình xử lý sự kiện được triển khai thực hiện với các lớp bên trong vô danh.
Mã này sinh ra chính xác cùng một kết quả giống như các phiên bản trước đó, nhưng nó sử dụng một cách tiếp cận phổ biến và đáng mong muốn hơn. Bạn sẽ hầu như luôn luôn thấy các trình xử lý sự kiện được triển khai thực hiện với các lớp bên trong vô danh.
Sử dụng các lớp lồng trong
Các lớp có lồng trong có thể rất có ích cho bạn. Chúng cũng có thể gây ra phiền toái.
Sử dụng một lớp lồng trong sẽ không ý nghĩa lắm khi có thể định nghĩa lớp ở bên ngoài của một lớp bao ngoài. Trong ví dụ của chúng ta, chúng ta đã có thể định nghĩa Wallet ở bên ngoài Adult mà không cảm thấy quá tệ. Nhưng hãy tưởng tượng một thứ gì đó kiểu như một lớp Personality. Bạn có bao giờ có một Personality bên ngoài một cá thể Person không?. Không, do đó hoàn toàn cần thiết phải định nghĩa Personality như là một lớp lồng trong. Một quy tắc ngón tay cái đúng đắn là bạn nên định nghĩa một lớp dưới dạng lớp không lồng trong cho đến khi rõ ràng là nó phải được lồng trong, sau đó cấu trúc lại để lồng nó.
Các lớp bên trong vô danh là cách tiếp cận tiêu chuẩn cho các trình xử lý sự kiện, vì vậy, hãy sử dụng chúng cho mục đích đó. Trong các trường hợp khác, cần thận trọng với chúng. Trừ khi các lớp bên trong vô danh là nhỏ, xoay quanh một việc, và quen thuộc, chúng làm cho mã khó hiểu. Chúng cũng có thể làm cho việc gỡ lỗi khó khăn hơn, mặc dù IDE của Eclipse giúp giảm thiểu sự phiền toái đó. Nói chung, hãy thử không sử dụng các lớp bên trong vô danh cho bất cứ thứ gì trừ các trình xử lý sự kiện.
Klik untuk melihat kode: :) =( :s :D :-D ^:D ^o^ 7:( :Q :p T_T @@, :-a :W *fck* x@