Thừa kế và đa hình trong Java
Công ty Vĩnh Cửu tuyển dụng lập trình viên Java

1- Giới thiệu

Thừa kế và đa hình - đây là một khái niệm vô cùng quan trọng trong Java. Mà bạn bắt buộc phải hiểu nó.

2- Class, cấu tử và đối tượng (Class, Constructor, instance)

Bạn cần hiểu một cách rạch ròi về class, cấu tử và đối tượng trước khi bắt đầu tìm hiểu quan hệ thừa kế trong java. Chúng ta xem class Person, mô tả một con người với các thông tin tên, năm sinh, năm mất.
  • Person.java
package org.o7planning.tutorial.inheritance.basic;

public class Person {

   // Trường name - thông tin tên người
   private String name;
   // Trường bornYear - thông tin năm sinh
   private Integer bornYear;
   // Trường diedYear - thông tin năm mất
   private Integer diedYear;

   // Cấu tử 3 tham số. Mục đích nhằm để khởi tạo các giá trị cho các trường
   // của Person.Chỉ định rõ tên, năm sinh, năm mất của một người.
   public Person(String name, Integer bornYear, Integer diedYear) {
       this.name = name;
       this.bornYear = bornYear;
       this.diedYear = diedYear;
   }

   // Cấu tử 2 tham số. Mục đích khởi tạo giá trị cho 2 trường tên và năm sinh
   // cho Person. Năm mất không được khởi tạo, nghĩa là người đang còn sống.
   public Person(String name, Integer bornYear) {
       this.name = name;
       this.bornYear = bornYear;
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public Integer getBornYear() {
       return bornYear;
   }

   public void setBornYear(Integer bornYear) {
       this.bornYear = bornYear;
   }

   public Integer getDiedYear() {
       return diedYear;
   }

   public void setDiedYear(Integer diedYear) {
       this.diedYear = diedYear;
   }
}
  • PersonDemo.java
package org.o7planning.tutorial.inheritance.basic;

public class PersonDemo {

   public static void main(String[] args) {

       // Đối tượng: Thomas Edison.
       Person edison = new Person("Thomas Edison", 1847, 1931);

       System.out.println("Info:");
       System.out.println("Name: " + edison.getName());
       System.out.println("Born Year: " + edison.getBornYear());
       System.out.println("Died Year: " + edison.getDiedYear());

       // Đối tượng: Bill Gates
       Person billGates = new Person("Bill Gate", 1955);

       System.out.println("Info:");
       System.out.println("Name: " + billGates.getName());
       System.out.println("Born Year: " + billGates.getBornYear());
       System.out.println("Died Year: " + billGates.getDiedYear());

   }

}

Phân biệt Class, cấu tử và đối tượng:

Class Person mô phỏng một lớp người, nó là một thứ gì đó trìu tượng, nhưng nó có các trường để mang thông tin, trong ví dụ trên là tên, năm sinh, năm mất.

Cấu tử (Constructor) - Người ta còn gọi là "Phương thức khởi tạo"
  • Cấu tử luôn có tên giống tên class
  • Một class có một hoặc nhiều cấu tử.
  • Cấu tử có hoặc không có tham số, cấu tử không có tham số còn gọi là cấu tử mặc định.
  • Cấu tử là cách để tạo ra một đối tượng của class.
Như vậy class Person (Mô tả lớp người) là thứ trìu tượng, nhưng khi chỉ rõ vào bạn hoặc tôi thì đó là 2 đối tượng (instance) thuộc class Person. Và cấu tử là phương thức đặc biệt để tạo ra đối tượng, cấu tử sẽ gán các giá trị vào các trường (field) của class cho đối tượng..

Hãy xem minh họa, khởi tạo đối tượng từ cấu tử như thế nào.

3- Thừa kế trong java

Chúng ta cần một vài class tham gia vào minh họa.
  • Animal: Class mô phỏng một lớp Động vật.
  • Duck: Class mô phỏng lớp vịt, là một class con của Animal.
  • Cat: Class mô phỏng lớp mèo, là một class con của Animal
  • Mouse: Class mô phỏng lớp chuột, là một class con của Animal.
Ở đây chúng ta có class Animal, với một method không có nội dung.
  • public abstract String getAnimalName();
Method này là một method trìu tượng (abstract), tại các class con cần phải khai báo và triển khai nội dung của nó. Method này có ý nghĩa là trả về tên loài động vật.
Class Animal có 1 phương thức trìu tượng nó phải được khai báo là trìu tượng (abstract). Class trìu tượng có các cấu tử nhưng bạn không thể khởi tạo đối tượng từ nó.
  • Về bản chất nghĩa là bạn muốn tạo một đối tượng động vật, bạn cần tạo từ một loại đông vật cụ thể, trong trường hợp này bạn phải khởi tạo từ cấu tử của Cat, Mouse hoặc Duck.
  • Animal.java
package org.o7planning.tutorial.inheritance.animal;

// Class có ít nhất 1 method trìu tượng phải khai báo là trìu tượng.
public abstract class Animal {

  // Tên, ví dụ Mèo Tom, Chuột Jerry.
  private String name;

  // Cấu tử mặc định
  public Animal() {
      // Gán tên mặc định
      this.name = this.getAnimalName();
  }

  public Animal(String name) {
      this.name = name;
  }

  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  // Trả về tên của loài động vật này.
  // Một method trìu tượng.
  // Nội dung cụ thể của method này được viết tại class con.
  public abstract String getAnimalName();

}

Tiếp theo xem class Cat, thừa kế từ Animal.

Cat cũng có các cấu tử của nó, và cũng có các trường của nó. Trong dòng đầu tiên của cấu tử bao giờ cũng phải gọi super(..) nghĩa là gọi lên cấu tử cha, để khởi tạo giá trị cho các trường của class cha.

Nếu bạn không gọi, mặc định Java hiểu là đã gọi super(), nghĩa là gọi cấu tử mặc định của class cha.
  • Cat.java
package org.o7planning.tutorial.inheritance.animal;

public class Cat extends Animal {

  private int age;
  private int height;

  public Cat(int age, int height) {
      // Gọi tới cấu tử mặc định của class cha (Animal)
      // Nhằm mục đích khởi tạo các trường trên class cha.
      super();
      // Sau đó mới tới việc khởi tạo giá trị cho các trường của nó
      this.age = age;
      this.height = height;
  }

  public Cat(String name, int age, int height) {
      // Gọi tới cấu tử của class cha (Animal)
      // Nhằm mục đích khởi tạo các trường trên class cha.
      super(name);
      // Sau đó mới tới việc khởi tạo giá trị cho các trường của nó
      this.age = age;
      this.height = height;
  }

  public int getAge() {
      return age;
  }

  public void setAge(int age) {
      this.age = age;
  }

  public int getHeight() {
      return height;
  }

  public void setHeight(int height) {
      this.height = height;
  }

  // Triển khai nội dung cho method trìu tượng khai báo tại class cha.
  @Override
  public String getAnimalName() {
      return "Cat";
  }

}

Khi bạn khởi tạo một đối tượng Cat điều gì xẩy ra?

Xem class Mouse, thừa kế từ Animal.

  • Mouse.java
package org.o7planning.tutorial.inheritance.animal;

public class Mouse extends Animal {

  private int weight;

  // Cấu tử mặc định
  public Mouse() {
      // Gọi tới cấu tử Mouse(int)
      this(100);
  }

  // Cấu tử 1 tham số
  public Mouse(int weight) {
      // Tại đây ko gọi super
      // Mặc định java hiểu gọi tới cấu tử mặc định của class cha.
      this.weight = weight;
  }

  // Cấu tử 2 tham số
  public Mouse(String name, int weight) {
      super(name);
      this.weight = weight;
  }

  public int getWeight() {
      return weight;
  }

  public void setWeight(int weight) {
      this.weight = weight;
  }

  @Override
  public String getAnimalName() {
      return "Mouse";
  }
}
  • InstanceofDemo.java
package org.o7planning.tutorial.inheritance.demo;

import org.o7planning.tutorial.inheritance.animal.Animal;
import org.o7planning.tutorial.inheritance.animal.Cat;
import org.o7planning.tutorial.inheritance.animal.Mouse;

public class InstanceofDemo {

  public static void main(String[] args) {

      // Khởi tạo một đối tượng động vật.
      // Animal là class trìu tượng
      // nó ko thể tạo đối tượng từ cấu tử của nó.
      Animal tom = new Cat("Tom", 3, 20);

      System.out.println("name: " + tom.getName());
      System.out.println("animalName: " + tom.getAnimalName());

      // Sử dụng toán tử instanceof để kiểm tra xem
      // một đối tượng có phải kiểu nào đó không.

      boolean isMouse = tom instanceof Mouse;
      System.out.println("Tom is mouse? " + isMouse);

      boolean isCat = tom instanceof Cat;
      System.out.println("Tom is cat? " + isCat);

      boolean isAnimal = tom instanceof Animal;
      System.out.println("Tom is animal? " + isAnimal);

  }

}
Kết quả chạy ví dụ:
  • InheritMethodDemo.java
package org.o7planning.tutorial.inheritance.demo;

import org.o7planning.tutorial.inheritance.animal.Cat;

public class InheritMethodDemo {

   public static void main(String[] args) {

       // Tạo một đối tượng Cat
       Cat tom = new Cat("Tom", 3, 20);
       
       
       // Gọi các method thừa kế được từ class cha.
       System.out.println("name: " + tom.getName());
       System.out.println("animalName: " + tom.getAnimalName());
       
       System.out.println("-----------------");

       // Gọi các method khai báo trên class Cat.
       System.out.println("Age: " + tom.getAge());
       System.out.println("Height: " + tom.getHeight());
   }

}
Kết quả chạy ví dụ:

Ép kiểu (Cast) trong Java

  • CastDemo.java
package org.o7planning.tutorial.inheritance.demo;

import java.util.Random;

import org.o7planning.tutorial.inheritance.animal.Animal;
import org.o7planning.tutorial.inheritance.animal.Cat;
import org.o7planning.tutorial.inheritance.animal.Mouse;

public class CastDemo {

   // Method trả về ngẫu nhiên một con vật.
   public static Animal getRandomAnimal() {
       // Trả về giá trị ngẫu nhiên 0,1
       int random = new Random().nextInt(2);

       Animal animal = null;
       if (random == 0) {
           animal = new Cat("Tom", 3, 20);
       } else {
           animal = new Mouse("Jerry", 5);
       }
       return animal;
   }

   public static void main(String[] args) {

       Animal animal = getRandomAnimal();

       if (animal instanceof Cat) {
           // Ép kiểu về Cat.
           Cat cat = (Cat) animal;

           // Và gọi method của class Cat.
           System.out.println("Cat height: " + cat.getHeight());
       } else if (animal instanceof Mouse) {
           // Ép kiểu về Mouse
           Mouse mouse = (Mouse) animal;

           // Và gọi method của class Mouse.
           System.out.println("Mouse weight: " + mouse.getWeight());
       }

   }

}

4- Tính đa hình trong Java

Bạn có một con mèo nguồn gốc châu Á (AsianCat), bạn có thể nói nó là một con mèo (Cat) hoặc nói nó là một con vật (Animal) đó là một khía cạnh của từ đa hình.

Hoặc một ví dụ khác: Trên lý lịch của bạn ghi rằng bạn là một người châu Á, trong khi đó bạn thực tế là một người Việt Nam. Và có thể rằng trong tương lai người ta sẽ cấp cho bạn một hộ chiếu khác ghi rằng bạn là người trái đất.

Ví dụ dưới đây cho bạn thấy cách hành sử giữa khai báo và thực tế
Class AsianCat là một class thừa kế từ Cat.
  • AsianCat.java
package org.o7planning.tutorial.inheritance.animal;

public class AsianCat extends Cat {

   public AsianCat(String name, int age, int height) {
       super(name, age, height);
   }

   // Ghi đè method của class cha (Cat)
   @Override
   public String getAnimalName() {
       return "Asian Cat";
   }
}
Tính đa hình của Java được gói gọi trong hình minh họa dưới đây:
  • PolymorphismDemo.java
package org.o7planning.tutorial.polymorphism;

import org.o7planning.tutorial.inheritance.animal.AsianCat;
import org.o7planning.tutorial.inheritance.animal.Cat;

public class PolymorphismDemo {

   public static void main(String[] args) {

       Cat cat1 = new Cat("Tom", 3, 20);

       Cat cat2 = new AsianCat("ATom", 2, 19);

       System.out.println("Animal Name of cat1: " + cat1.getAnimalName());

       System.out.println("Animal Name of cat2: " + cat2.getAnimalName());

   }

}
Kết quả chạy ví dụ: