Tính Đa hình (Polymorphism) trong Java - Java OOP
Thu, 17 Apr 2025

Khám phá các công cụ và kỹ thuật hữu ich cho Lập trình viên
Giới thiệu
Trong lập trình hướng đối tượng (OOP), tính trừu tượng là một trong bốn trụ cột chính, cùng với tính đóng gói (encapsulation), tính kế thừa (inheritance) và tính đa hình (polymorphism). Tính trừu tượng tập trung vào việc che giấu các chi tiết triển khai phức tạp và chỉ hiển thị các tính năng cần thiết của đối tượng cho thế giới bên ngoài.
Hãy tưởng tượng bạn đang lái một chiếc xe hơi. Bạn chỉ cần biết cách sử dụng vô lăng, chân ga, chân phanh và cần số. Bạn không cần quan tâm đến cơ chế hoạt động phức tạp bên trong động cơ, hệ thống truyền động hay hệ thống điện tử. Đó chính là trừu tượng – bạn tương tác với một giao diện đơn giản hóa mà không cần biết chi tiết bên trong.
Trong Java, tính trừu tượng giúp:
Cách đạt được Tính trừu tượng trong Java
Java cung cấp hai cơ chế chính để triển khai tính trừu tượng:
Chúng ta sẽ đi sâu vào từng loại.
Một lớp trừu tượng là một lớp được khai báo với từ khóa abstract
. Nó có các đặc điểm sau:
new
. Mục đích của nó là để làm lớp cơ sở (base class) cho các lớp khác kế thừa.abstract
và không có phần thân (implementation). Các lớp con cụ thể (concrete subclasses - không phải abstract) kế thừa từ lớp trừu tượng này bắt buộc phải cung cấp triển khai (override) cho tất cả các phương thức trừu tượng đó.super()
).Khi nào sử dụng Lớp trừu tượng?
public static final
).Ví dụ về Lớp trừu tượng:
// Định nghĩa lớp trừu tượng Shape
abstract class Shape {
String color; // Biến instance có thể được chia sẻ
// Constructor
public Shape(String color) {
System.out.println("Constructor của Shape được gọi");
this.color = color;
}
// Phương thức cụ thể
public String getColor() {
return color;
}
// Phương thức trừu tượng - không có thân hàm
// Các lớp con BẮT BUỘC phải triển khai phương thức này
public abstract double calculateArea();
// Một phương thức cụ thể khác
public void displayInfo() {
System.out.println("Đây là một hình có màu: " + color);
}
}
// Lớp Circle kế thừa từ Shape
class Circle extends Shape {
double radius;
public Circle(String color, double radius) {
super(color); // Gọi constructor của lớp cha (Shape)
System.out.println("Constructor của Circle được gọi");
this.radius = radius;
}
// Bắt buộc phải triển khai phương thức trừu tượng calculateArea()
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
// Có thể override phương thức cụ thể nếu muốn
@Override
public void displayInfo() {
super.displayInfo(); // Gọi phương thức của lớp cha
System.out.println("Loại hình: Tròn, Bán kính: " + radius);
}
}
// Lớp Rectangle kế thừa từ Shape
class Rectangle extends Shape {
double width;
double height;
public Rectangle(String color, double width, double height) {
super(color);
System.out.println("Constructor của Rectangle được gọi");
this.width = width;
this.height = height;
}
// Bắt buộc phải triển khai phương thức trừu tượng calculateArea()
@Override
public double calculateArea() {
return width * height;
}
}
public class AbstractionDemo {
public static void main(String[] args) {
// Shape myShape = new Shape("Red"); // Lỗi! Không thể khởi tạo lớp trừu tượng
Shape circle = new Circle("Đỏ", 5.0);
Shape rectangle = new Rectangle("Xanh", 4.0, 6.0);
System.out.println("\nThông tin hình tròn:");
circle.displayInfo();
System.out.println("Diện tích hình tròn: " + circle.calculateArea());
System.out.println("Màu sắc: " + circle.getColor());
System.out.println("\nThông tin hình chữ nhật:");
rectangle.displayInfo();
System.out.println("Diện tích hình chữ nhật: " + rectangle.calculateArea());
System.out.println("Màu sắc: " + rectangle.getColor());
}
}
Trong ví dụ này, Shape
là lớp trừu tượng định nghĩa cấu trúc chung (có màu sắc, phải tính được diện tích). Circle
và Rectangle
là các lớp cụ thể cung cấp cách triển khai riêng cho việc tính diện tích.
Một giao diện trong Java là một bản thiết kế (blueprint) hoàn toàn trừu tượng của một lớp. Nó được khai báo bằng từ khóa interface
. Đặc điểm chính:
public abstract
.public static final
.implements
để "thực thi" một hoặc nhiều interface. Lớp này bắt buộc phải cung cấp triển khai cho tất cả các phương thức trừu tượng được định nghĩa trong interface đó (trừ khi lớp đó cũng là abstract class).default methods
(phương thức có phần thân, có thể được override) và static methods
(phương thức có phần thân, thuộc về interface, không thể override).private methods
và private static methods
để hỗ trợ việc tái sử dụng code bên trong các default và static methods của chính interface đó.Khi nào sử dụng Interface?
Runnable
, Comparable
, Serializable
).Ví dụ về Interface:
// Định nghĩa interface Drawable
interface Drawable {
// Biến trong interface mặc định là public static final
String DEFAULT_COLOR = "Black";
// Phương thức trừu tượng (mặc định là public abstract)
void draw();
// Default method (từ Java 8)
default void printInfo() {
System.out.println("Đây là một đối tượng có thể vẽ được.");
privateMethodHelper(); // Gọi phương thức private (từ Java 9)
}
// Static method (từ Java 8)
static int getNumberOfCorners(String shapeType) {
switch (shapeType.toLowerCase()) {
case "circle": return 0;
case "square": return 4;
default: return -1; // Không xác định
}
}
// Private method (từ Java 9) - chỉ dùng nội bộ trong interface
private void privateMethodHelper() {
System.out.println("Thông tin bổ sung từ private method.");
}
}
// Định nghĩa interface Clickable
interface Clickable {
void onClick();
}
// Lớp Button triển khai cả Drawable và Clickable
class Button implements Drawable, Clickable {
String label;
public Button(String label) {
this.label = label;
}
// Bắt buộc triển khai phương thức draw() từ Drawable
@Override
public void draw() {
System.out.println("Vẽ nút bấm: [" + label + "]");
}
// Bắt buộc triển khai phương thức onClick() từ Clickable
@Override
public void onClick() {
System.out.println("Nút '" + label + "' đã được nhấn!");
}
// Có thể override default method nếu muốn
@Override
public void printInfo() {
System.out.println("Đây là một Button có thể vẽ và nhấn.");
}
}
// Lớp Image chỉ triển khai Drawable
class Image implements Drawable {
String sourceFile;
public Image(String sourceFile) {
this.sourceFile = sourceFile;
}
// Bắt buộc triển khai phương thức draw() từ Drawable
@Override
public void draw() {
System.out.println("Vẽ hình ảnh từ file: " + sourceFile);
}
}
public class InterfaceDemo {
public static void main(String[] args) {
// Gọi static method của interface
System.out.println("Số góc của hình vuông: " + Drawable.getNumberOfCorners("Square"));
Drawable buttonDrawable = new Button("OK");
Clickable buttonClickable = new Button("Cancel"); // Cùng đối tượng nhưng tham chiếu kiểu khác
Button myButton = new Button("Submit");
Drawable image = new Image("logo.png");
System.out.println("\n--- Button ---");
buttonDrawable.draw(); // Gọi draw() qua tham chiếu Drawable
buttonClickable.onClick(); // Gọi onClick() qua tham chiếu Clickable
myButton.printInfo(); // Gọi printInfo() (đã override)
myButton.onClick(); // Gọi onClick() qua tham chiếu Button
System.out.println("\n--- Image ---");
image.draw();
image.printInfo(); // Gọi default method printInfo() từ interface
// Truy cập hằng số từ interface
System.out.println("\nMàu mặc định: " + Drawable.DEFAULT_COLOR);
}
}
Trong ví dụ này, Drawable
và Clickable
định nghĩa các "khả năng". Lớp Button
có cả hai khả năng này, trong khi Image
chỉ có khả năng Drawable
.
Nên chọn cái nào?
public static final
.Thu, 17 Apr 2025
Thu, 17 Apr 2025
Sat, 22 Mar 2025
Để lại một bình luận