Java – Inheritance

Inheritance (kế thừa) trong Java là một khái niệm cốt lõi trong lập trình hướng đối tượng. Kế thừa được sử dụng khi chúng ta có quan hệ is-a giữu các object. Nó cho phép chúng ta mang những đặc tính của class cha sang class con. Tính năng này giúp tích kiệm thời gian cũng như dữ liệu dư thừa, lượng code giảm. Kế thừa được thực thi bằng cách sử dụng từ khóa extends

Kế thừa có tính chất bắc cầu, nghĩa là Sedan extends CarCar extends Vehicle thì cũng có nghĩa là Sedan sẽ kế thừa từ class Vehicle.  Lúc này thì Vehicle trở thành superclass của cả CarSedan.

Kế thừa được sử dụng rộng rãi trong các ứng dụng Java, ví dụ chúng ta có thể mở rộng thêm class Exception để tạo một class Exception cụ thể cho ứng dụng mà sẽ chứa nhiều thông tin hơn là chỉ có các mã lỗi. Ví dụ NullPointerException.

Ví dụ

Tất cả các class trong Java được ngầm định extends từ class java.lang.Object. Do đó class Object nằm ở cấp cao nhất của hệ thống phân cấp kế thừa trong Java.

Chúng ta hãy cùng xem ví dụ sau đây về kế thừa trong Java

Superclass: Animal

package com.example.inheritance;

public class Animal {

  private boolean vegetarian;
  private String eats;
  private int noOfLegs;

  public Animal(){}

  public Animal(boolean veg, String food, int legs){
    this.vegetarian = veg;
    this.eats = food;
    this.noOfLegs = legs;
  }

  public boolean isVegetarian() {
    return vegetarian;
  }

  public void setVegetarian(boolean vegetarian) {
    this.vegetarian = vegetarian;
  }

  public String getEats() {
    return eats;
  }

  public void setEats(String eats) {
    this.eats = eats;
  }

  public int getNoOfLegs() {
    return noOfLegs;
  }

  public void setNoOfLegs(int noOfLegs) {
    this.noOfLegs = noOfLegs;
  }
}

Ở đây, Animal là class cơ sở. Hãy tạo một class Cat kế thừa từ class Animal

Subclass: Cat

package com.example.inheritance;

public class Cat extends Animal{

  private String color;

  public Cat(boolean veg, String food, int legs) {
    super(veg, food, legs);
    this.color="White";
  }

  public Cat(boolean veg, String food, int legs, String color){
    super(veg, food, legs);
    this.color=color;
  }

  public String getColor() {
    return color;
  }

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

Chú ý rằng sử dụng từ khóa extends để thực thi kế thừa trong Java

Viết chương trình test

Hãy viết một class test đơn giản để tạo Cat object và sử dụng một vài phương thức của nó

package com.example.inheritance;

public class AnimalInheritanceTest {

  public static void main(String[] args) {
    Cat cat = new Cat(false, "milk", 4, "black");

    System.out.println("Cat is Vegetarian?" + cat.isVegetarian());
    System.out.println("Cat eats " + cat.getEats());
    System.out.println("Cat has " + cat.getNoOfLegs() + " legs.");
    System.out.println("Cat color is " + cat.getColor());
  }
}

Kết quả

Cat is Vegetarian?false
Cat eats milk
Cat has 4 legs.
Car color is black

Class Cat không có phương thức getEats() nhưng vẫn chạy bởi vì nó kế thừa từ class Animal

Ưu điểm của Inheritance

  1. Khả năng sử dụng lại code là lợi ích quan trọng nhất khi sử dụng kế thừa bởi vì subclass kế thừa các biến và phương thức của superclass
  2. Các thành phần Private của superclass sẽ không thể truy cập trực tiếp bởi subclass. Ví dụ biến noOfLegs không thể truy cập từ class Cat, nhưng nó có thể được truy cập một cách gián tiếp sử dụng các phương thức gettersetter
  3. Các thành phần của superclass với các truy cập mặc định chỉ có thể truy cập được bởi subclass nếu chúng có cùng chung package
  4. Constructor của superclass không được kế thừa bởi subclass
  5. Nếu superclass không có constructor mặc định thì subclass cũng cần có một định nghĩa constructor rõ ràng, nếu không nó sẽ đưa ra một Exception khi biên dịch. Trong trường hợp này, trong constructor của subclass bắt buộc phải gọi constructor của superclass và nó nên là câu lệnh đầu tiên ở trong constructor của subclass.
  6. Java không hỗ trợ nhiều kế thừa, một subclass chỉ có thể extends từ một class. Animal ngầm kế thừa từ class ObjectCat được kế thừa từ class Animal, nhưng do tính bắc cầu của kế thừa trong Java, nên class Cat cũng kế thừa từ class Object
  7. Chúng ta có thể tạo một instance của subclass sau đó gán nó cho một biến của superclass, đây được gọi là upcasting, Sau đây là ví dụ của upcasting
    Cat c = new Cat(); //subclass instance
    Animal a = c; //upcasting, nó hoạt động tốt vì Cat cũng là một Animal
  8. Khi một instance của superclass được gán cho một biến của subclass thì được gọi là downcasting, chúng ta cần chỉ định kiểu rõ ràng cho subclass, sau đây là ví dụ về downcasting
    Cat c = new Cat();
    Animal a = c;
    Cat c1 = (Cat) a; //chỉ định kiểu rõ ràng subclass, chạy tốt bởi vì"c" chắc chắn là kiểu của Cat

    Chú ý rằng trình biên dịch sẽ không thông báo thậm trí khi chúng ta đang thực hiện sai khi dùng chỉ định kiểu rõ ràng. Sau đây là một vài trường hợp sẽ bị lỗi trong thời gian chạy.

    Dog d = new Dog();
    Animal a = d;
    Cat c1 = (Cat) a; //ClassCastException khi chạy
    
    Animal a1 = new Animal();
    Cat c2 = (Cat) a1; //ClassCastException bởi vì a1 chắc chắn kiểu Animal khi chạy
  9. Chúng ta có thể viết đè các phương thức của superclass trong subclass. Tuy nhiên chúng ta nên chú thích các phương thức bị ghi đè bằng  @Override annotation. Trình biên dịch sẽ không biết được rằng chúng ta đang ghi đè một phương thức. Nếu có một vài thứ thay đổi nào trong phương thức của superclass, chúng ta nên nhận được lỗi khi chạy biên dịch hơn là nhận được kết quả không mong muốn khi chạy.  Chúng ta có thể gọi các phương thức superclass và truy cập các biến của superclass sử dụng từ khóa super. Nó rất là hữu ích khi chúng ta có cùng tên các phương thức và biến trong subclass  nhưng chúng ta muốn truy cập biến và phương thức của superclass. Nó cũng được sử dụng khi các constructor được định nghĩa trong superclass và subclass, chúng ta phải gọi chính xác constructor của subclass.
  10. Chúng ta có thể sử dụng chỉ thị instanceof để kiểm tra kế thừa giữa các object, hãy cùng xem ví dụ bên dưới đây
    Cat c = new Cat();
    Dog d = new Dog();
    Animal an = c;
    
    boolean flag = c instanceof Cat; //trường hợp thông thường, trả về true
    
    flag = c instanceof Animal; // trả về true vì c cũng là is-an Animal
    
    flag = an instanceof Cat; //trả về true vì a kiểu của Cat khi chạy
    
    flag = a instanceof Dog; //trả về false với lý do rõ ràng.
  11. Chúng ta không thể extends final class trong Java
  12. Nếu bạn sẽ không sử dụng superclass trong code ví dụ superclass của bạn chỉ là một cơ sở để giúp sử dụng lại code thì bạn nên đặt nó trong một abstract class để tránh những khởi tạo không cần thiết bởi các class phía client. Nó cũng hạn chế tạo instance của class cơ sở.

You May Also Like

About the Author: Nguyen Dinh Thuc

Leave a Reply

Your email address will not be published.