JavaRush /Blog Java /Random-VI /Các lớp bên trong lồng nhau hoặc Lớp bên trong trong Java...

Các lớp bên trong lồng nhau hoặc Lớp bên trong trong Java

Xuất bản trong nhóm
Xin chào! Hôm nay chúng ta sẽ bắt đầu xem xét một chủ đề quan trọng - các lớp lồng nhau hoạt động như thế nào trong Java. Trong tiếng Anh chúng được gọi là các lớp lồng nhau. Java cho phép bạn tạo một số lớp bên trong các lớp khác:
class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
Chính những lớp 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 - 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 theo một cách khác.
  2. Các lớp lồng nhau tĩnh - các lớp lồng nhau tĩnh.
Đổi lại, các lớp bên trong có hai kiểu con đặc biệt. Bên cạnh thực tế là lớp bên trong có thể chỉ là lớp bên trong, nó cũng có thể là:
  • lớp học địa phương
  • lớp ẩn danh
Một chút khó khăn? :) Không sao đâu, đây là sơ đồ cho rõ ràng. Hãy quay lại với nó trong giờ giảng nếu bạn chợt cảm thấy bối rối! Các lớp bên trong lồng nhau - 2Trong bài giảng hôm nay chúng ta sẽ nói về Inner class – các lớp bên trong (chúng cũng là các lớp lồng nhau không tĩnh, các lớp lồng nhau không tĩnh). Chúng được đánh dấu đặc biệt trong sơ đồ chung để bạn không bị lạc :) Hãy bắt đầu với câu hỏi rõ ràng: tại sao các lớp này được gọi là “nội bộ”? Câu trả lời khá đơn giản: 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("Go!");
   }

   public class HandleBar {

       public void right() {
           System.out.println("Steering wheel to the right!");
       }

       public void left() {

           System.out.println("Steering wheel to the left!");
       }
   }

   public class Seat {

       public void up() {

           System.out.println("The seat is up!");
       }

       public void down() {

           System.out.println("The seat is down!");
       }
   }
}
Ở đây chúng tôi có một lớp học Bicycle- xe đạp. Nó có 2 trường và 1 phương thức - start(). Các lớp bên trong lồng nhau - 3Sự khác biệt của nó so với một lớp thông thường là nó có hai lớp, mã được viết bên trong Bicycle- đây là các lớp HandleBar(vô lăng) và Seat(ghế ngồi). Đây là các lớp chính thức: như bạn có thể thấy, mỗi lớp đều có các phương thức riêng. Tại thời điểm này, bạn có thể thắc mắc: tại sao chúng ta lại đặt một lớp này vào một lớp khác? Tại sao làm cho chúng nội bộ? Được rồi, giả sử chúng ta cần các lớp riêng biệt cho vô lăng và ghế ngồi trong chương trình. Nhưng bạn không cần phải lồng chúng! Bạn có thể tổ chức các lớp học thông thường. Ví dụ như thế này:
public class HandleBar {
   public void right() {
       System.out.println("Steering wheel to the right!");
   }

   public void left() {

       System.out.println("Steering wheel left");
   }
}

public class Seat {

   public void up() {

       System.out.println("The seat is up!");
   }

   public void down() {

       System.out.println("The seat is down!");
   }
}
Câu hỏi rất hay! Tất nhiên, chúng tôi không có giới hạn kỹ thuật - chúng tôi có thể làm theo cách này. Đó là việc thiết kế các lớp học một cách chính xác theo quan điểm của một chương trình cụ thể và theo ý nghĩa của chương trình đó. Các lớp bên trong là các lớp dùng để làm nổi bật một thực thể nhất định trong một chương trình được liên kết chặt chẽ với một thực thể khác. Vô lăng, ghế ngồi, bàn đạp là những bộ phận của một chiếc xe đạp. Tách khỏi xe đạp, chúng không có ý nghĩa gì. Nếu chúng ta tách tất cả các lớp này thành các lớp công khai riêng biệt, thì chương trình của chúng ta có thể có đoạn mã sau:
public class Main {

   public static void main(String[] args) {
       HandleBar handleBar = new HandleBar();
       handleBar.right();
   }
}
Ừm... Ý nghĩa của đoạn mã này thậm chí còn khó giải thích hơn. Chúng tôi có một số tay lái xe đạp kỳ lạ (tại sao lại cần nó? Thành thật mà nói, tôi không biết). Và cái vô lăng này quay sang phải... tự nó, không có xe đạp... vì lý do nào đó. Bằng cách tách rời bản chất của vô lăng khỏi bản chất của chiếc xe đạp, chúng ta đã đánh mất tính logic của chương trình. Sử dụng lớp bên trong, mã trông hoàn toàn 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 của bảng điều khiển:

Сиденье поднято выше!
Поехали!
Руль влево!
Руль вправо!
Những gì đang xảy ra đột nhiên có ý nghĩa! :) Chúng tôi đã tạo ra một đối tượng xe đạp. Chúng tôi đã tạo ra hai “đối tượng phụ” của nó - vô lăng và ghế ngồi. Chúng tôi đã nâng ghế lên cao hơn để thuận tiện - và chúng tôi khởi hành: chúng tôi lăn bánh và lái đến nơi chúng tôi cần đến! :) Các phương thức chúng ta cần được gọi trên các đối tượng cần thiết. Mọi thứ đều đơn giản và thuận tiện. Trong ví dụ này, việc làm nổi bật tay lái và ghế ngồi sẽ tăng cường khả năng đóng gói (chúng tôi đang ẩn dữ liệu về các bộ phận của xe đạp trong lớp tương ứng) và cho phép chúng tôi tạo ra 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ô hình một cửa hàng xe đạp và phụ tùng. Các lớp bên trong lồng nhau - 4Trong tình huống này, giải pháp trước đó của chúng tôi sẽ thất bại. Trong giới hạn của một cửa hàng phụ tùng, mỗi bộ phận riêng lẻ của một chiếc xe đạp đều có ý nghĩa thậm chí ngoài bản chất của chiếc xe đạp. Ví dụ: chúng ta sẽ cần các phương thức như “bán bàn đạp cho người mua”, “mua ghế mới”, v.v. Sẽ là một sai lầm nếu sử dụng các lớp nội bộ ở đây - mỗi bộ phận riêng lẻ của chiếc xe đạp trong chương trình mới của chúng tôi đều có ý nghĩa riêng: nó tách biệt với bản chất của chiếc xe đạp và không hề gắn liền với nó. Đây là điều bạn nên chú ý nếu bạn đang thắc mắc liệu mình có cần sử dụng các lớp bên trong hay tách tất cả các thực thể thành các lớp riêng biệt hay không. Lập trình hướng đối tượng rất tuyệt vời vì nó giúp dễ dàng mô hình hóa các thực thể trong thế giới thực. Đây là những gì bạn có thể sử dụng làm hướng dẫn khi quyết định có nên sử dụng các lớp bên trong hay không. Trong cửa hàng thực, các bộ phận được tách biệt khỏi xe đạp - điều này là bình thường. Điều này có nghĩa là điều này sẽ đúng khi thiết kế một chương trình. Được rồi, chúng ta đã sắp xếp xong “triết học” :) Bây giờ 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 bên trong không thể tồn tại nếu không có đối tượng của lớp “bên ngoài”.

    Điều này rất hợp lý: đó là lý do tại sao chúng tôi tạo nó thành các lớp Seatnội bộ HandleBar, để vô lăng và ghế ngồi không có chủ sở hữu sẽ không xuất hiện chỗ này chỗ kia trong chương trình của chúng tôi.

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

    public static void main(String[] args) {
    
       HandleBar handleBar = new HandleBar();
    }

    Tính năng quan trọng sau đây xuất phát từ điều nà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 Bicyclemột biến vào lớp của chúng ta int seatPostDiameter- đường kính của cột an toàn.

    Sau đó, trong lớp bên trong, Seatchúng ta có thể tạo một phương thức getSeatParam()cho chúng ta biết tham số 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("Go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("The seat is up!");
           }
    
           public void down() {
    
               System.out.println("The seat is down!");
           }
    
           public void getSeatParam() {
    
               System.out.println("Seat parameter: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }

    Và bây giờ chúng ta có thể lấy 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.getSeatParam();
       }
    }

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

    
    Параметр сиденья: диаметр подседельного штыря = 40

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

  3. Không thể tạo đối tượng lớp bên trong theo 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 thiết kế của các lớp nội bộ. Một lớp bên trong có thể có các hàm tạo với các tham số hoặc chỉ là một hàm tạo mặc định. Nhưng bất chấp điều này, khi chúng ta tạo một đối tượng của lớp bên trong, một tham chiếu đến một đối tượng của lớp “bên ngoài” sẽ được truyền vào nó một cách lặng lẽ. Suy cho cùng, sự hiện diện của một đối tượng như vậy là điều kiện tiên quyết. 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 phương thức của lớp bên ngoài là tĩnh thì đối tượng của lớp bên ngoài có thể không tồn tại! Điều này có nghĩa là logic của lớp bên trong sẽ bị phá vỡ. Trong tình huống như vậy, trình biên dịch sẽ đưa ra lỗi:

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

    Logic ở đây giống nhau: các phương thức và biến tĩnh có thể tồn tại và được gọi ngay cả khi không có đối tượng.

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

    Một sự mâu thuẫn rõ ràng! Vì vậy, sự hiện diện của các biến và phương thức tĩnh trong các lớp bên trong đều bị cấm.

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

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

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

    Tại sao nó lại quan trọng?

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

    Nếu lớp của chúng ta Seatđược khai báo là public, chúng ta có thể tạo các đối tượng của nó 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 đã thực hiện việc 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 truy cập lớp bên trong HandleBartừ tệp Main.

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

    SeatChúng ta sẽ không thể tạo một đối tượng từ bên ngoài nữa:

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

    Có lẽ bạn đã 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 cụ sửa đổi protectedcung cấp quyền truy cập vào một biến lớp trong các lớp con của nó và trong các lớp nằm trong cùng một gói.

    Điều tương tự cũng protectedxảy ra với các lớp bên trong. Các đối tượng protectedlớp bên trong có thể được tạo:

    • bên trong lớp "bên ngoài";
    • trong các lớp con cháu 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

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

    Bạn đã làm quen với các sửa đổi từ lâu nên sẽ không có vấn đề gì ở đây.

Bây giờ chỉ vậy thôi :) Nhưng đừng thư giãn! Các lớp lồng nhau bên trong là một chủ đề khá rộng mà chúng ta sẽ tiếp tục khám phá trong các bài học sau. Bây giờ bạn có thể tiếp tục bài giảng về các lớp học nội bộ từ khóa học của chúng tôi. Và lần tới chúng ta sẽ nói về các lớp lồng nhau tĩnh.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION