CodeGym/Blog Java/Ngẫu nhiên/Ví dụ về kế thừa của các lớp lồng nhau

Ví dụ về kế thừa của các lớp lồng nhau

Xuất bản trong nhóm
CHÀO! Hôm nay chúng ta sẽ xem xét một cơ chế quan trọng: kế thừa trong các lớp lồng nhau. Bạn đã bao giờ nghĩ về những gì bạn sẽ làm nếu bạn cần tạo một lớp lồng nhau kế thừa một số lớp khác chưa. Nếu không, hãy tin tôi: tình huống này có thể gây nhầm lẫn, bởi vì có rất nhiều sắc thái.
  1. Chúng ta có đang tạo một lớp lồng nhau kế thừa một số lớp không? Hay chúng ta đang làm cho một số lớp kế thừa một lớp lồng nhau?
  2. Lớp con/lớp cha là lớp công khai bình thường hay nó cũng là lớp lồng nhau?
  3. Cuối cùng, loại lớp lồng nhau nào chúng ta sử dụng trong tất cả các tình huống này?
Có rất nhiều câu trả lời khả thi cho tất cả những câu hỏi này, đầu bạn sẽ quay cuồng :) Như bạn đã biết, chúng ta có thể giải quyết một vấn đề phức tạp bằng cách chia nó thành các phần đơn giản hơn. Hãy làm điều đó. Chúng ta hãy lần lượt xem xét từng nhóm lớp lồng nhau từ hai quan điểm: ai có thể kế thừa từng loại lớp lồng nhau và lớp đó có thể kế thừa ai. Hãy bắt đầu với các lớp lồng nhau tĩnh.

Các lớp lồng nhau tĩnh

Ví dụ kế thừa lớp lồng nhau - 2Quy tắc kế thừa của họ là đơn giản nhất. Ở đây bạn có thể làm hầu hết mọi thứ mà trái tim bạn mong muốn. Một lớp lồng tĩnh có thể kế thừa:
  • một lớp học bình thường
  • một lớp lồng tĩnh được khai báo trong một lớp bên ngoài hoặc tổ tiên của nó
Nhớ lại một ví dụ từ bài học của chúng ta về các lớp lồng nhau tĩnh.
public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
Hãy thử thay đổi mã và tạo một Drawinglớp lồng nhau tĩnh và hậu duệ của nó — Boeing737Drawing.
public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }

   public static class Boeing737Drawing extends Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
Như bạn có thể thấy, không có vấn đề gì. Chúng ta thậm chí có thể kéo Drawinglớp đó ra và biến nó thành một lớp công khai bình thường thay vì một lớp lồng nhau tĩnh - sẽ không có gì thay đổi.
public class Drawing {

}

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Boeing737Drawing extends Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
Chúng tôi hiểu điều này. Nhưng những lớp nào có thể kế thừa một lớp lồng tĩnh? Thực tế là bất kỳ! Lồng/không lồng, tĩnh/không tĩnh — không thành vấn đề. Ở đây chúng ta làm cho Boeing737Drawinglớp bên trong kế thừa Drawinglớp lồng tĩnh:
public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }

   public class Boeing737Drawing extends Drawing {

       public int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
Bạn có thể tạo một ví dụ Boeing737Drawingnhư thế này:
public class Main {

   public static void main(String[] args) {

      Boeing737 boeing737 = new Boeing737(1990);
      Boeing737.Boeing737Drawing drawing = boeing737.new Boeing737Drawing();
      System.out.println(drawing.getMaxPassengersCount());

   }

}
Mặc dù Boeing737Drawinglớp của chúng ta kế thừa một lớp tĩnh nhưng bản thân nó không tĩnh! Kết quả là, nó sẽ luôn cần một thể hiện của lớp bên ngoài. Chúng ta có thể xóa Boeing737Drawinglớp khỏi Boeing737lớp và biến nó thành một lớp công khai đơn giản. Không có gì thay đổi. Nó vẫn có thể kế thừa Drawinglớp lồng nhau tĩnh.
public class Boeing737 {

   private int manufactureYear;
   public static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }
}

public class Boeing737Drawing extends Boeing737.Drawing {

   public int getMaxPassengersCount() {

       return Boeing737.maxPassengersCount;

}
Điểm quan trọng duy nhất là trong trường hợp này, chúng ta cần đặt maxPassengersCountbiến tĩnh ở chế độ công khai. Nếu nó vẫn ở chế độ riêng tư, thì một lớp công khai bình thường sẽ không có quyền truy cập vào nó. Chúng tôi đã tìm ra các lớp tĩnh! :) Bây giờ hãy chuyển sang các lớp bên trong. Chúng có 3 loại: lớp bên trong đơn giản, lớp cục bộ và lớp bên trong ẩn danh. Ví dụ kế thừa lớp lồng nhau - 3Một lần nữa, hãy chuyển từ đơn giản sang phức tạp :)

Các lớp bên trong ẩn danh

Một lớp bên trong ẩn danh không thể kế thừa một lớp khác. Không có lớp nào khác có thể kế thừa một lớp ẩn danh. Nó không thể đơn giản hơn! :)

lớp địa phương

Các lớp cục bộ (trong trường hợp bạn quên) được khai báo bên trong một khối mã của lớp khác. Thông thường, điều này xảy ra bên trong một số phương thức của lớp bên ngoài. Về mặt logic, chỉ các lớp cục bộ khác bên trong cùng một phương thức (hoặc khối mã) mới có thể kế thừa một lớp cục bộ. Đây là một ví dụ:
public class PhoneNumberValidator {

   public void validatePhoneNumber(final String number) {

       class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       class CellPhoneNumber extends PhoneNumber {

       }

       class LandlinePhoneNumber extends PhoneNumber {


       }

       // ...number validation code
   }
}
Đây là đoạn mã từ bài học của chúng ta về các lớp cục bộ. Lớp trình xác nhận số của chúng tôi có một PhoneNumberlớp cục bộ. Nếu chúng ta cần nó để đại diện cho hai thực thể riêng biệt, ví dụ: số điện thoại di động và số điện thoại cố định, thì chúng ta chỉ có thể thực hiện việc này trong cùng một phương thức. Lý do rất đơn giản: phạm vi của một lớp cục bộ được giới hạn ở phương thức (khối mã) nơi nó được khai báo. Do đó, chúng tôi sẽ không thể sử dụng nó bên ngoài (bao gồm cả kế thừa lớp). Tuy nhiên, khả năng kế thừa trong chính lớp cục bộ rộng hơn nhiều! Một lớp cục bộ có thể kế thừa:
  1. Một lớp học bình thường.
  2. Một lớp bên trong được khai báo trong cùng một lớp với lớp cục bộ hoặc trong một trong các tổ tiên của nó.
  3. Một lớp cục bộ khác được khai báo trong cùng một phương thức (khối mã).
Điểm thứ nhất và thứ ba có vẻ rõ ràng, nhưng điểm thứ hai hơi khó hiểu :/ Hãy xem xét hai ví dụ. Ví dụ 1 — "Làm cho một lớp cục bộ kế thừa một lớp bên trong được khai báo trong cùng một lớp với lớp cục bộ":
public class PhoneNumberValidator {

   class PhoneNumber {

       private String phoneNumber;

       public PhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }

       public String getPhoneNumber() {
           return phoneNumber;
       }

       public void setPhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }
   }

   public void validatePhoneNumber(final String number) {

       class CellPhoneNumber extends PhoneNumber {

           public CellPhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       class LandlinePhoneNumber extends PhoneNumber {

           public LandlinePhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       // ...number validation code
   }
}
Ở đây chúng tôi đã loại bỏ PhoneNumberlớp khỏi validatePhoneNumber()phương thức và biến nó thành lớp bên trong thay vì lớp cục bộ. Điều này không ngăn chúng ta làm cho 2 lớp cục bộ của chúng ta kế thừa nó. Ví dụ 2 - "... hoặc trong tổ tiên của lớp này." Bây giờ điều này đã thú vị hơn. Chúng ta có thể tiến PhoneNumbercao hơn nữa trong chuỗi thừa kế. Hãy khai báo một AbstractPhoneNumberValidatorlớp trừu tượng, lớp này sẽ trở thành tổ tiên của PhoneNumberValidatorlớp chúng ta:
public abstract class AbstractPhoneNumberValidator {

   class PhoneNumber {

       private String phoneNumber;

       public PhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }

       public String getPhoneNumber() {
           return phoneNumber;
       }

       public void setPhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }
   }

}
Như bạn có thể thấy, chúng tôi không chỉ khai báo nó — chúng tôi còn chuyển PhoneNumberlớp bên trong vào đó. Tuy nhiên, trong lớp kế thừa của nó PhoneNumberValidator, các lớp cục bộ được khai báo trong các phương thức có thể kế thừa PhoneNumbermà không gặp vấn đề gì!
public class PhoneNumberValidator extends AbstractPhoneNumberValidator {

   public void validatePhoneNumber(final String number) {

       class CellPhoneNumber extends PhoneNumber {

           public CellPhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       class LandlinePhoneNumber extends PhoneNumber {

           public LandlinePhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       // ...number validation code
   }
}
Do mối quan hệ kế thừa, các lớp cục bộ bên trong lớp hậu duệ "nhìn thấy" các lớp bên trong bên trong lớp tổ tiên. Và cuối cùng, hãy tiến tới nhóm cuối cùng :)

lớp học bên trong

Một lớp bên trong được khai báo trong cùng một lớp bên ngoài (hoặc trong hậu duệ của nó) có thể kế thừa một lớp bên trong khác. Hãy khám phá điều này bằng cách sử dụng ví dụ về xe đạp trong bài học về các lớp học bên trong.
public class Bicycle {

   private String model;
   private int maxWeight;

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

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

   class Seat {

       public void up() {

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

       public void down() {

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

   class SportSeat extends Seat {

       // ...methods
   }
}
Ở đây chúng tôi đã khai báo Seatlớp bên trong bên trong Bicyclelớp. Một loại ghế đua đặc biệt, SportSeatkế thừa nó. Tuy nhiên, chúng ta có thể tạo một loại "xe đạp đua" riêng biệt và đặt nó trong một lớp riêng biệt:
public class SportBicycle extends Bicycle {

   public SportBicycle(String model, int maxWeight) {
       super(model, maxWeight);
   }


   class SportSeat extends Seat {

       public void up() {

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

       public void down() {

           System.out.println("Seat down!");
       }
   }
}
Đây cũng là một lựa chọn. Lớp bên trong của hậu duệ ( SportBicycle.SportSeat) "nhìn thấy" các lớp bên trong của tổ tiên và có thể kế thừa chúng. Kế thừa các lớp bên trong có một tính năng rất quan trọng! Trong hai ví dụ trước, SportSeatlớp của chúng tôi là một lớp bên trong. Nhưng điều gì sẽ xảy ra nếu chúng ta quyết định tạo SportSeatmột lớp công khai thông thường đồng thời kế thừa Seatlớp bên trong?
// Error! No enclosing instance of type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {

   public SportSeat() {

   }

   public void up() {

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

   public void down() {

       System.out.println("Seat down!");
   }
}
Chúng tôi gặp lỗi! Bạn có đoán được tại sao không? :) Tất cả đều đơn giản. Khi chúng ta nói về Bicycle.Seatlớp bên trong, chúng ta đã đề cập rằng một tham chiếu đến một thể hiện của lớp bên ngoài được chuyển hoàn toàn cho hàm tạo của lớp bên trong. Điều này có nghĩa là bạn không thể tạo Seatđối tượng mà không tạo Bicycleđối tượng. Nhưng những gì về việc tạo ra một SportSeat? Không giống như Seat, nó không có cơ chế tích hợp sẵn này để chuyển hoàn toàn hàm tạo tham chiếu đến một thể hiện của lớp bên ngoài. Cho đến khi không có Bicycleđối tượng, chúng ta không thể tạo SportSeatđối tượng, giống như trường hợp của Seat. Do đó, chúng ta chỉ còn một việc phải làm — chuyển rõ ràng cho SportSeathàm tạo một tham chiếu đến một Bicycleđối tượng. Đây là cách thực hiện:
class SportSeat extends Bicycle.Seat {

   public SportSeat(Bicycle bicycle) {

       bicycle.super();
   }

   public void up() {

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

   public void down() {

       System.out.println("Seat down!");
   }
}
Chúng ta gọi hàm tạo của lớp bậc trên bằng cách sử dụng super(); Bây giờ, nếu chúng ta muốn tạo một SportSeatđối tượng, không gì có thể ngăn chúng ta làm điều này:
public class Main {

   public static void main(String[] args) {

       Bicycle bicycle = new Bicycle("Peugeot", 120);
       SportSeat peugeotSportSeat = new SportSeat(bicycle);

   }
}
Phù! Bài học này khá dài :) Nhưng bạn đã học được rất nhiều! Bây giờ là lúc để giải quyết một số nhiệm vụ! :)
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