CHÀO! Hãy nói về các lớp trừu tượng trong Java.Ví dụ cụ thể về lớp trừu tượng trong Java - 1

Tại sao các lớp được gọi là "trừu tượng"?

Bạn có thể nhớ trừu tượng là gì — chúng ta đã thảo luận về nó trước đó :) Nếu bạn quên, đừng lo lắng. Hãy nhớ rằng, nguyên tắc OOP nói rằng, khi thiết kế các lớp và tạo các đối tượng, bạn chỉ nên biểu diễn các thuộc tính chính của thực thể và loại bỏ các thuộc tính phụ. Ví dụ: nếu chúng tôi đang thiết kế một SchoolTeacherlớp học, chiều cao có thể không phải là thuộc tính cần thiết của giáo viên. Thật vậy, đặc điểm này không quan trọng đối với một giáo viên. Nhưng nếu chúng ta đang tạo một BasketballPlayerlớp, thì chiều cao sẽ là một trong những đặc điểm quan trọng nhất. Vâng, một lớp trừu tượnglà “phôi thô” trừu tượng nhất, dành cho một nhóm lớp tương lai. Phôi không thể được sử dụng trực tiếp — nó quá "thô". Nhưng nó xác định trạng thái và hành vi đặc trưng nhất định mà các lớp trong tương lai - hậu duệ của lớp trừu tượng - sẽ có.

Ví dụ về các lớp trừu tượng trong Java

Hãy xem xét một ví dụ đơn giản với ô tô:

public abstract class Car {

   private String model;
   private String color;
   private int maxSpeed;
  
   public abstract void gas();

   public abstract void brake();

   public String getModel() {
       return model;
   }

   public void setModel(String model) {
       this.model = model;
   }

   public String getColor() {
       return color;
   }

   public void setColor(String color) {
       this.color = color;
   }

   public int getMaxSpeed() {
       return maxSpeed;
   }

   public void setMaxSpeed(int maxSpeed) {
       this.maxSpeed = maxSpeed;
   }
}
Đây là giao diện của lớp trừu tượng đơn giản nhất. Như bạn có thể thấy, không có gì đặc biệt :) Tại sao chúng ta cần cái này? Đầu tiên, nó cung cấp mô tả trừu tượng nhất về thực thể mà chúng ta cần - một chiếc ô tô. Từ khóa trừu tượng có nghĩa là một cái gì đó ở đây. Trong thế giới thực, không có thứ gọi là "chỉ là một chiếc ô tô". Có xe tải, xe đua, sedan, coupe và SUV. Lớp trừu tượng của chúng ta chỉ đơn giản là một "bản thiết kế" mà sau này chúng ta sẽ sử dụng để tạo các lớp ô tô cụ thể.

public class Sedan extends Car {
  
   @Override
   public void gas() {
       System.out.println("The sedan is accelerating!");
   }

   @Override
   public void brake() {
       System.out.println("The sedan is slowing down!");
   }
  
}
Theo nhiều cách, điều này tương tự như những gì chúng ta đã nói trong các bài học về thừa kế. Chỉ trong trường hợp đó, chúng ta mới có một Carlớp có các phương thức không trừu tượng. Nhưng một giải pháp như vậy có một số nhược điểm được khắc phục trong các lớp trừu tượng. Đầu tiên và quan trọng nhất, không thể tạo một thể hiện của một lớp trừu tượng:

public class Main {

   public static void main(String[] args) {

       Car car = new Car(); // Error! The Car class is abstract!
   }
}
Người tạo ra Java đã cố tình tạo ra "tính năng" này. Một lần nữa, hãy nhớ rằng: lớp trừu tượng chỉ là bản thiết kế cho các lớp "thông thường" trong tương lai . Bạn không cần bản sao của một kế hoạch chi tiết, phải không? Tương tự, không cần tạo các thể hiện của một lớp trừu tượng :) Và nếu lớp Carkhông trừu tượng, thì chúng ta có thể dễ dàng tạo các thể hiện của nó:

public class Car {

   private String model;
   private String color;
   private int maxSpeed;
  
   public void go() {
       // ...some logic
   }

   public  void brake() {
       // ...some logic
   }
}


public class Main {

   public static void main(String[] args) {

       Car car = new Car(); // This is okay. The car is created.
   }
}
Hiện tại, chương trình của chúng tôi có một số loại xe khó hiểu. Nó không phải là một chiếc xe tải, không phải là một chiếc xe đua, và không phải là một chiếc sedan, nhưng thực sự không rõ nó là gì. Đó là "chỉ là một chiếc xe hơi" không tồn tại trong thực tế. Ví dụ tương tự có thể được đưa ra với động vật. Hãy tưởng tượng nếu chương trình của bạn có Animalcác đối tượng (" chỉ động vật "). Không rõ nó là loài gì, thuộc họ gì hay có đặc điểm gì. Sẽ là lạ khi nhìn thấy một trong một chương trình. Không có "động vật đơn thuần" trong tự nhiên. Chỉ chó, mèo, cáo, chuột chũi, v.v. Các lớp trừu tượng cứu chúng ta khỏi " chỉ đối tượng ". Họ cung cấp cho chúng tôi trạng thái và hành vi cơ bản. Ví dụ: tất cả ô tô phải có kiểu dáng , màu sắctốc độ tối đa, và họ cũng phải có khả năng nhấn gaphanh . Đó là nó. Đó là một bản thiết kế trừu tượng chung mà bạn sẽ sử dụng sau này để thiết kế các lớp bạn cần. Lưu ý: hai phương thức trong lớp trừu tượng cũng là trừu tượng , có nghĩa là chúng không có triển khai nào cả. Lý do là như nhau: các lớp trừu tượng không tạo ra "các hành vi mặc định" cho "chỉ ô tô". Họ chỉ chỉ ra những gì mọi chiếc xe phải có khả năng làm. Điều đó nói rằng, nếu bạn cần hành vi mặc định, bạn có thể triển khai các phương thức trong một lớp trừu tượng. Java không cấm điều này:

public abstract class Car {

   private String model;
   private String color;
   private int maxSpeed;

   public void gas() {
       System.out.println("Accelerating!");
   }

   public abstract void brake();
  
   // Getters and setters
}


public class Sedan extends Car {

   @Override
   public void brake() {
       System.out.println("The sedan is slowing down!");
   }

}

public class Main {

   public static void main(String[] args) {

       Sedan sedan = new Sedan();
       sedan.gas();
   }
}
Đầu ra bảng điều khiển:
Accelerating!
Như bạn có thể thấy, chúng tôi đã triển khai một phương thức trong lớp trừu tượng, nhưng không phải phương thức kia. Kết quả là, hành vi của Sedanlớp chúng ta được chia thành hai phần: nếu chúng ta gọi gas()phương thức của nó, thì hành vi đó được "kéo lên" từ Carlớp cha trừu tượng và chúng ta triển khai brake()phương thức đó trong Sedanlớp. Đó là siêu thuận tiện và linh hoạt. Nhưng bây giờ lớp của chúng ta không quá trừu tượng, phải không ? Rốt cuộc, nó thực sự đã thực hiện một nửa số phương pháp. Thực tế là - và đây là một tính năng rất quan trọng - một lớp là trừu tượng nếu ngay cả một trong các phương thức của nó là trừu tượng. Một trong hai phương pháp, hoặc một trong một nghìn - không thành vấn đề. Chúng tôi thậm chí có thể thực hiện tất cả các phương thức, không để lại phần trừu tượng nào. Kết quả sẽ là một lớp trừu tượng không có bất kỳ phương thức trừu tượng nào. Về nguyên tắc, điều này có thể xảy ra - trình biên dịch sẽ không tạo ra bất kỳ lỗi nào - nhưng tốt hơn hết là không nên làm điều này, vì nó làm mất đi ý nghĩa của từ trừu tượng. Các lập trình viên đồng nghiệp của bạn cũng sẽ rất ngạc nhiên khi thấy điều này:/ Điều đó nói rằng, nếu một phương thức được đánh dấu là trừu tượng, thì mỗi lớp hậu duệ phải triển khai nó hoặc được khai báo là trừu tượng. Nếu không, trình biên dịch sẽ báo lỗi. Tất nhiên, mỗi lớp chỉ có thể kế thừa một lớp trừu tượng nên không có sự khác biệt giữa lớp trừu tượng và lớp thông thường về tính kế thừa. Không quan trọng chúng ta kế thừa một lớp trừu tượng hay lớp thông thường — chỉ có thể có một lớp cha.

Tại sao Java không có kế thừa nhiều lớp

Chúng tôi đã nói rằng không có đa kế thừa trong Java, nhưng chúng tôi chưa thực sự đi sâu vào lý do tại sao. Hãy thử làm điều đó ngay bây giờ. Thực tế là nếu Java có nhiều kế thừa, thì các lớp con sẽ không thể quyết định nên chọn hành vi nào. Giả sử chúng ta có hai lớp: ToasterNuclearBomb:

public class Toaster {
  
  
 public void on() {

       System.out.println("The toaster is on. We're toasting!");
   }
  
   public void off() {

       System.out.println("The toaster is off!");
   }
}


public class NuclearBomb {

   public void on() {

       System.out.println("Boom!");
   }
}
Như bạn có thể thấy, cả hai lớp đều có một on()phương thức. Đối với máy nướng bánh mì, phương pháp này bắt đầu làm bánh mì nướng, nhưng trong trường hợp bom hạt nhân, nó sẽ gây ra vụ nổ. Uh-oh :/ Bây giờ hãy tưởng tượng rằng bạn đã quyết định (đừng hỏi tôi tại sao!) để tạo ra thứ gì đó ở giữa. Đây là lớp học của bạn: MysteriousDevice! Tất nhiên, mã này sẽ không hoạt động. Chúng tôi trình bày nó đơn giản như một ví dụ về "những gì có thể đã xảy ra":

public class MysteriousDevice extends Toster, NuclearBomb {

   public static void main(String[] args) {
      
       MysteriousDevice mysteriousDevice = new MysteriousDevice();
       mysteriousDevice.on(); // And what should happen here? Will we get toast or a nuclear apocalypse?
   }
}
Hãy xem những gì chúng tôi đã có. Thiết bị bí ẩn bắt nguồn từ cả Toaster và NuclearBomb cùng một lúc. Cả hai đều có một on()phương pháp. Do đó, không rõ việc triển khai nào sẽ được thực thi nếu chúng ta gọi on()một MysteriousDeviceđối tượng. Đối tượng sẽ không hiểu. Và trên hết, NuclearBomb không có off()phương pháp, vì vậy nếu chúng ta không đoán đúng, thì sẽ không thể tắt thiết bị. Ví dụ cụ thể về lớp trừu tượng trong Java - 2"Sự hiểu lầm" này, khi không rõ hành vi nào nên được thực thi, chính là lý do tại sao những người tạo ra Java từ chối đa kế thừa. Điều đó nói rằng, bạn sẽ biết rằng các lớp Java có thể triển khai nhiều giao diện.