1. Tính đa hình (polymorphism) trong Java là gì?
Tính đa hình (polymorphism) hiểu đơn giản là các đối tượng, các phương thức giống nhau có thể có các hành vi khác nhau tùy vào từng tình huống khác nhau.
class Polygon {
//phương thức render của lớp Polygon
public void render() {
System.out.println("Rendering Polygon...");
}
}
class Square extends Polygon {
//ghi đè phương thức render
@Override
public void render() {
System.out.println("Rendering Square...");
}
}
class Circle extends Polygon {
//ghi đè phương thức render
@Override
public void render() {
System.out.println("Rendering Circle...");
}
}
class Main {
public static void main(String[] args) {
// create an object of Square
Square s1 = new Square();
s1.render();
// create an object of Circle
Circle c1 = new Circle();
c1.render();
}
}
Kết quả
Rendering Square...
Rendering Circle...
Ở ví dụ trên, lớp Polygon có 2 lớp con là Square and Circle. Chúng đều có hàm render()
nhưng hàm này thực thi khác nhau trong từng lớp. Đó là biểu hiện của tính đa hình (polymorphism).
2. Tại sao sử dụng tính đa hình (polymorphism)
Tính đa hình (polymorphism) trong Java cho phép chúng ta tạo ra những mã code nhất quán. Lấy ví dụ ở phần 1, chúng ta có thể tạo ra các hàm render với các tên khác nhau như renderSquare() và renderCircle() cho từng lớp Square và Circle.
Chương trình của bạn cũng sẽ hoạt động tốt thôi nhưng việc tạo ra các tên khác nhau cho một hàm có chức năng giống nhau thì sẽ làm code không nhất quán, rườm rà, cồng kềnh.
Tính đa hình cho phép chúng ta tạo ra một hàm tên là render()
duy nhất nhưng sẽ thực thi khác nhau với từng lớp khác nhau.
Trong Java, chúng ta có thể đạt được tính đa hình (polymorphism) với nạp chồng phương thức (method overloading), ghi đè phương thức (method overriding).
Lưu ý: Trong Java không hỗ trợ lập trình viên tự định nghĩa nạp chồng toán tử (operator overloading) như trong C++.
3. Chuyển đổi kiểu dữ liệu đối tượng
Kiểu dữ liệu tham chiếu (các lớp) có thể được chuyển đổi kiểu khi kiểu dữ liệu tham chiếu (lớp) tương thích, tức là nằm trên cùng một cây phân cấp kế thừa.
Có hai cách chuyển đổi là Up-casting và Down-casting.
Up-casting
– Đi lên trên cây phân cấp thừa kế. Up-casting là khả năng nhìn nhận đối tượng thuộc lớp dẫn xuất như là một đối tượng thuộc lớp cơ sở.
– Tự động chuyển đổi kiểu.
Ví dụ:
class Person{
String name;
Date birthday;
public void setName(String name){
this.name = name;
}
public void setBirthday(Date birthday){
this.birthday = birthday;
}
public String getDetails(){
String detail = "";
detail = "Name: " + name + ", birthday: " + new SimpleDateFormat("MM-dd-yyyy").format(birthday);
return detail;
}
}
class Employee extends Person{
double salary;
public void setSalary(double salary){
this.salary = salary;
}
@Override
public String getDetails(){
String detail="";
detail = "Name: " + name + ", birthday: " + new SimpleDateFormat("MM-dd-yyyy").format(birthday) + ", salary: " + salary;
return detail;
}
}
class Main {
public static void main(String[] args) throws ParseException {
Employee e = new Employee();
Person p;
p = e;
p.setName("Henry");
p.setBirthday(new SimpleDateFormat("MM-dd-yyyy").parse("12-31-1998"));
System.out.println(p.getDetails());
}
}
Kết quả
Name: Henry, birthday: 12-31-1998, salary: 0.0
Trong ví dụ trên, chúng ta tạo ra đối tượng p của lớp Person. Mà lớp Person là lớp cha của lớp Employee. Một Employee cũng là một Person. Nên p có thể tham chiếu đến đối tượng của lớp Employee với câu lệnh p = e;
. Các hàm mà Person p gọi là những hàm mà một Employee kế thừa được từ Person.
Lưu ý, một Person chưa chắc là một Employee bởi có thể có những lớp khác cũng kế thừa lớp Person như lớp Student, Teacher. Thì lúc này, Person cũng có thể là Student hay Teacher. Nhưng một Student hay Teacher thì chắc chắn vẫn là một Person.
Lưu ý tiếp theo, nếu đối tượng p của lớp Person gọi hàm setSalary()
của đối tượng e thuộc lớp Employee thì sẽ báo lỗi. Bởi vì một đối tượng người thì không có setSalary()
. Tức là, Employee e bây giờ là 1 Person bình thường.
p.setSalary(7000.0);//error
Down-casting
– Đi xuống cây phân cấp thừa kế. Down-casting là khả năng nhìn nhận một đối tượng thuộc lớp cơ sở như một đối tượng thuộc lớp dẫn xuất.
– Không tự động chuyển đổi kiểu, phải ép kiểu.
class Person{
String name;
Date birthday;
public void setName(String name){
this.name = name;
}
public void setBirthday(Date birthday){
this.birthday = birthday;
}
public String getDetails(){
String detail = "";
detail = "Name: " + name + ", birthday: " + new SimpleDateFormat("MM-dd-yyyy").format(birthday);
return detail;
}
}
class Employee extends Person{
double salary;
public void setSalary(double salary){
this.salary = salary;
}
@Override
public String getDetails(){
String detail="";
detail = "Name: " + name + ", birthday: " + new SimpleDateFormat("MM-dd-yyyy").format(birthday) + ", salary: " + salary;
return detail;
}
}
class Main {
public static void main(String[] args) throws ParseException {
Person p1 = new Employee();
//down-casting
Employee e1 = (Employee) p1;
e1.setName("Henry");
e1.setBirthday(new SimpleDateFormat("MM-dd-yyyy").parse("12-31-1998"));
e1.setSalary(7000.0);
System.out.println(e1.getDetails());
}
}
Kết quả
Name: Henry, birthday: 12-31-1998, salary: 7000.0
Tóm lại, Up-casting là gán object của lớp con cho biến tham chiếu của lớp cha. Down-casting là gán object của lớp cha cho biến tham chiếu của lớp con.
4. Toán tử instanceof
Toán tử instanceof
trong Java được sử dụng để kiểm tra một đối tượng có phải là thể hiện của một kiểu dữ liệu (lớp) nào đó hay không.
instanceof
trong Java được gọi là toán tử so sánh kiểu. Nó trả về giá trị boolean là true hoặc false. Nếu bạn dùng toán tử instanceof
với bất kỳ biến nào mà có giá trị null thì kết quả trả về sẽ là false. Ví dụ:
public class Simple {
public static void main(String args[]) {
Simple s = new Simple();
System.out.println(s instanceof Simple);// true
}
}
Một đối tượng có kiểu của lớp con thì cũng có kiểu của lớp cha.
Ví dụ: Nếu Dog kế thừa từ Animal thì đối tượng của Dog có thể tham chiếu đến cả hai lớp Dog và Animal.
class Animal {}
public class Dog extends Animal {// Dog inherits Animal
public static void main(String args[]) {
Dog dog = new Dog();
System.out.println(dog instanceof Animal);// true
}
}
Nếu sử dụng toán tử instanceof
với biến có kiểu bất kỳ có giá trị null thì giá trị trả về luôn là null. Ví dụ:
public class Dog {
public static void main(String args[]) {
Dog d = null;
System.out.println(d instanceof Dog);// false
}
}