CodeGym/Blog Java/Ngẫu nhiên/Các lớp bên trong lồng nhau

Các lớp bên trong lồng nhau

Xuất bản trong nhóm
CHÀO! Hôm nay chúng ta sẽ tiếp tục một chủ đề quan trọng - các lớp lồng nhau hoạt động như thế nào trong Java. Java cho phép bạn tạo các lớp bên trong một lớp khác:
class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
Các lớp nội bộ này được gọi là lồng nhau. Chúng được chia thành 2 loại:
  1. Các lớp lồng nhau không tĩnh. Chúng còn được gọi là các lớp bên trong.
  2. Các lớp lồng nhau tĩnh.
Đổi lại, các lớp bên trong có hai danh mục con riêng biệt. Ngoài một lớp bên trong chỉ đơn giản là một lớp bên trong, nó cũng có thể là:
  • một lớp địa phương
  • một lớp ẩn danh
Bối rối? :) Không sao đâu. Đây là một sơ đồ cho rõ ràng. Hãy quay lại với nó trong bài học nếu bạn đột nhiên thấy mình bối rối! Các lớp bên trong lồng nhau - 2Trong bài học hôm nay, chúng ta sẽ thảo luận về lớp bên trong (hay còn gọi là lớp lồng nhau không tĩnh). Chúng được đánh dấu đặc biệt trong sơ đồ tổng thể để bạn không bị lạc :) Hãy bắt đầu với câu hỏi rõ ràng: tại sao chúng được gọi là các lớp "bên trong"? Câu trả lời khá đơn giản: bởi vì chúng được tạo bên trong các lớp khác. Đây là một ví dụ:
public class Bicycle {

   private String model;
   private int weight;

   public Bicycle(String model, int weight) {
       this.model = model;
       this.weight = weight;
   }

   public void start() {
       System.out.println("Let's go!");
   }

   public class Handlebar {

       public void right() {
           System.out.println("Steer right!");
       }

       public void left() {

           System.out.println("Steer left!");
       }
   }

   public class Seat {

       public void up() {

           System.out.println("Seat up!");
       }

       public void down() {

           System.out.println("Seat down!");
       }
   }
}
Ở đây chúng ta có Bicyclelớp học. Nó có 2 trường và 1 phương thức: start(). Các lớp bên trong lồng nhau - 3Nó khác với một lớp thông thường ở chỗ nó chứa hai lớp: HandlebarSeat. Mã của họ được viết bên trong Bicyclelớp. Đây là các lớp chính thức: như bạn có thể thấy, mỗi lớp có các phương thức riêng. Tại thời điểm này, bạn có thể có một câu hỏi: tại sao trên thế giới chúng ta lại đặt một lớp bên trong một lớp khác? Tại sao làm cho chúng lớp bên trong? Chà, giả sử chúng ta cần các lớp riêng biệt cho các khái niệm về tay lái và chỗ ngồi trong chương trình của mình. Tất nhiên, chúng ta không cần thiết phải lồng chúng vào nhau! Chúng ta có thể làm các lớp bình thường. Ví dụ, như thế này:
public class Handlebar {
   public void right() {
       System.out.println("Steer right!");
   }

   public void left() {

       System.out.println("Steer left");
   }
}

public class Seat {

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
Câu hỏi rất hay! Tất nhiên, chúng tôi không bị giới hạn bởi công nghệ. Làm điều đó chắc chắn là một lựa chọn. Ở đây, điều quan trọng hơn là thiết kế chính xác của các lớp từ quan điểm của một chương trình cụ thể và mục đích của nó. Các lớp bên trong là để tách ra một thực thể được kết nối chặt chẽ với một thực thể khác. Tay lái, ghế ngồi và bàn đạp là các bộ phận của xe đạp. Tách khỏi xe đạp, chúng không có nhiều ý nghĩa. Nếu chúng ta tách tất cả các khái niệm này thành các lớp công khai, chúng ta sẽ có mã như thế này trong chương trình của mình:
public class Main {

   public static void main(String[] args) {
       Handlebar handlebar = new Handlebar();
       handlebar.right();
   }
}
Hmm... Ý nghĩa của mã này thậm chí còn khó giải thích. Chúng tôi có một số tay lái mơ hồ (Tại sao nó lại cần thiết? Thành thật mà nói, tôi không biết). Và cái tay cầm này rẽ phải... tự nó, không có xe đạp... vì lý do nào đó. Bằng cách tách biệt khái niệm tay lái khỏi khái niệm xe đạp, chúng ta đã làm mất đi một số logic trong chương trình của mình. Sử dụng một lớp bên trong, mã trông rất khác:
public class Main {

   public static void main(String[] args) {

       Bicycle peugeot = new Bicycle("Peugeot", 120);
       Bicycle.Handlebar handlebar = peugeot.new Handlebar();
       Bicycle.Seat seat = peugeot.new Seat();

       seat.up();
       peugeot.start();
       handlebar.left();
       handlebar.right();
   }
}
Đầu ra bảng điều khiển:
Seat up!
Let's go!
Steer left!
Steer right!
Bây giờ những gì chúng ta thấy đột nhiên có ý nghĩa! :) Chúng tôi đã tạo một đối tượng xe đạp. Chúng tôi đã tạo ra hai "đối tượng phụ" của xe đạp — tay lái và yên xe. Chúng tôi nâng ghế lên để tạo sự thoải mái và bắt đầu: đạp và lái khi cần thiết! :) Các phương thức chúng ta cần được gọi trên các đối tượng thích hợp. Tất cả đều đơn giản và thuận tiện. Trong ví dụ này, việc tách riêng tay lái và yên xe giúp tăng cường khả năng đóng gói (chúng tôi ẩn dữ liệu về các bộ phận xe đạp bên trong lớp có liên quan) và cho phép chúng tôi tạo một bản tóm tắt chi tiết hơn. Bây giờ chúng ta hãy xem xét một tình huống khác. Giả sử chúng ta muốn tạo một chương trình mô phỏng một cửa hàng bán xe đạp và phụ tùng cho xe đạp. Các lớp bên trong lồng nhau - 4Trong tình huống này, giải pháp trước đây của chúng tôi sẽ không hoạt động. Tại một cửa hàng xe đạp, mỗi bộ phận riêng lẻ của xe đạp đều có ý nghĩa ngay cả khi tách rời khỏi xe đạp. Ví dụ: chúng tôi sẽ cần các phương thức như "bán bàn đạp cho khách hàng", "mua ghế mới", v.v. Sẽ là sai lầm nếu sử dụng các lớp bên trong ở đây — mỗi bộ phận xe đạp riêng lẻ trong chương trình mới của chúng tôi đều có ý nghĩa đứng trên riêng của nó: nó có thể được tách ra khỏi khái niệm về một chiếc xe đạp. Đây chính là điều bạn cần chú ý nếu đang phân vân không biết nên sử dụng các lớp bên trong hay tổ chức tất cả các thực thể thành các lớp riêng biệt. Lập trình hướng đối tượng tốt ở chỗ nó giúp dễ dàng mô hình hóa các thực thể trong thế giới thực. Đây có thể là nguyên tắc hướng dẫn của bạn khi quyết định có nên sử dụng các lớp bên trong hay không. Trong một cửa hàng thực tế, phụ tùng thay thế riêng biệt với xe đạp - điều này không sao cả. Điều này có nghĩa là nó cũng không sao khi thiết kế một chương trình. Được rồi, chúng ta đã tìm ra "triết lý" :) Bây giờ chúng ta hãy làm quen với các tính năng "kỹ thuật" quan trọng của các lớp bên trong. Đây là những gì bạn chắc chắn cần phải nhớ và hiểu:
  1. Một đối tượng của lớp trong không thể tồn tại nếu không có đối tượng của lớp ngoài.

    Điều này có ý nghĩa: đây là lý do tại sao chúng tôi tạo SeatHandlebarcác lớp bên trong chương trình của mình — để chúng tôi không kết thúc với tay lái và ghế ngồi mồ côi.

    Mã này không biên dịch:

    public static void main(String[] args) {
    
       Handlebar handlebar = new Handlebar();
    }

    Một tính năng quan trọng khác sau đây:

  2. Một đối tượng của lớp bên trong có quyền truy cập vào các biến của lớp bên ngoài.

    Ví dụ: hãy thêm một int seatPostDiameterbiến (đại diện cho đường kính của cột an toàn) vào Bicyclelớp của chúng ta.

    Sau đó, trong Seatlớp bên trong, chúng ta có thể tạo một displaySeatProperties()phương thức hiển thị các thuộc tính chỗ ngồi:

    public class Bicycle {
    
       private String model;
       private int weight;
    
       private int seatPostDiameter;
    
       public Bicycle(String model, int weight, int seatPostDiameter) {
           this.model = model;
           this.weight = weight;
           this.seatPostDiameter = seatPostDiameter;
    
       }
    
       public void start() {
           System.out.println("Let's go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("Seat up!");
           }
    
           public void down() {
    
               System.out.println("Seat down!");
           }
    
           public void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }

    Và bây giờ chúng ta có thể hiển thị thông tin này trong chương trình của mình:

    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
           Bicycle.Seat seat = bicycle.new Seat();
    
           seat.displaySeatProperties();
       }
    }

    Đầu ra bảng điều khiển:

    Seat properties: seatpost diameter = 40

    Ghi chú:biến mới được khai báo với công cụ sửa đổi truy cập nghiêm ngặt nhất ( private). Và lớp bên trong vẫn có quyền truy cập!

  3. Một đối tượng của lớp bên trong không thể được tạo trong phương thức tĩnh của lớp bên ngoài.

    Điều này được giải thích bởi các tính năng cụ thể về cách tổ chức các lớp bên trong. Một lớp bên trong có thể có các hàm tạo có tham số hoặc chỉ hàm tạo mặc định. Nhưng bất chấp điều đó, khi chúng ta tạo một đối tượng của lớp bên trong, một tham chiếu đến đối tượng của lớp bên ngoài được chuyển một cách vô hình tới đối tượng đã tạo của lớp bên trong. Rốt cuộc, sự hiện diện của một tham chiếu đối tượng như vậy là một yêu cầu tuyệt đối. Nếu không, chúng ta sẽ không thể tạo các đối tượng của lớp bên trong.

    Nhưng nếu một phương thức của lớp bên ngoài là tĩnh, thì chúng ta có thể không có đối tượng của lớp bên ngoài! Và điều này sẽ vi phạm logic về cách thức hoạt động của một lớp bên trong. Trong trường hợp này, trình biên dịch sẽ báo lỗi:

    public static Seat createSeat() {
    
       // Bicycle.this cannot be referenced from a static context
       return new Seat();
    }
  4. Một lớp bên trong không thể chứa các biến và phương thức tĩnh.

    Logic là như nhau: các phương thức và biến tĩnh có thể tồn tại và được gọi hoặc tham chiếu ngay cả khi không có đối tượng.

    Nhưng không có đối tượng của lớp bên ngoài, chúng ta sẽ không truy cập được vào lớp bên trong.

    Một sự mâu thuẫn rõ ràng! Đây là lý do tại sao các biến và phương thức tĩnh không được phép sử dụng trong các lớp bên trong.

    Trình biên dịch sẽ báo lỗi nếu bạn cố tạo chúng:

    public class Bicycle {
    
       private int weight;
    
    
       public class Seat {
    
           // An inner class cannot have static declarations
           public static void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
  5. Khi tạo một đối tượng của lớp bên trong, công cụ sửa đổi truy cập của nó rất quan trọng.

    Một lớp bên trong có thể được đánh dấu bằng các công cụ sửa đổi truy cập tiêu chuẩn: public, private, protectedpackage private.

    Vì sao vấn đề này?

    Điều này ảnh hưởng đến nơi chúng ta có thể tạo các thể hiện của lớp bên trong chương trình của mình.

    Nếu Seatlớp của chúng ta được khai báo là public, chúng ta có thể tạo Seatcác đối tượng trong bất kỳ lớp nào khác. Yêu cầu duy nhất là một đối tượng của lớp bên ngoài cũng phải tồn tại.

    Nhân tiện, chúng tôi đã làm điều này ở đây:

    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle peugeot = new Bicycle("Peugeot", 120);
           Bicycle.Handlebar handlebar = peugeot.new Handlebar();
           Bicycle.Seat seat = peugeot.new Seat();
    
           seat.up();
           peugeot.start();
           handlebar.left();
           handlebar.right();
       }
    }

    Chúng tôi dễ dàng có được quyền truy cập vào Handlebarlớp bên trong từ Mainlớp.

    Nếu chúng ta khai báo lớp bên trong là private, chúng ta sẽ chỉ có thể tạo các đối tượng bên trong lớp bên ngoài.

    Chúng ta không còn có thể tạo một Seatđối tượng "ở bên ngoài":

    private class Seat {
    
       // Methods
    }
    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
    
           // Bicycle.Seat has private access in Bicycle
           Bicycle.Seat seat = bicycle.new Seat();
       }
    }

    Chắc bạn cũng đã hiểu logic rồi :)

  6. Công cụ sửa đổi truy cập cho các lớp bên trong hoạt động giống như đối với các biến thông thường.

    Công protectedcụ sửa đổi cung cấp quyền truy cập vào một biến thể hiện trong các lớp con và các lớp trong cùng một gói.

    protectedcũng hoạt động cho các lớp bên trong. Chúng ta có thể tạo protectedcác đối tượng của lớp bên trong:

    • ở lớp ngoài;
    • trong các lớp con của nó;
    • trong các lớp nằm trong cùng một gói.

    Nếu lớp bên trong không có công cụ sửa đổi truy cập ( package private), các đối tượng của lớp bên trong có thể được tạo:

    • ở lớp ngoài;
    • trong các lớp nằm trong cùng một gói.

    Bạn đã quen thuộc với các công cụ sửa đổi trong một thời gian dài, vì vậy không có vấn đề gì ở đây.

Đó là tất cả cho bây giờ :) Nhưng đừng buông lơi! Các lớp bên trong là một chủ đề khá rộng mà chúng ta sẽ tiếp tục khám phá trong bài học tiếp theo. Bây giờ bạn có thể làm mới trí nhớ của mình về bài học trong khóa học của chúng tôi về các lớp bên trong . Và lần tới, hãy nói về các lớp lồng nhau tĩnh.
Bình luận
  • Phổ biến
  • Mới
Bạn phải đăng nhập để đăng nhận xet
Trang này chưa có bất kỳ bình luận nào