Đối với phần đầu tiên của bạn trong những câu hỏi tôi nghĩ Wikipedia cung cấp một định nghĩa tốt:
Trong lập trình hướng đối tượng, kiểu phụ đa hình hoặc bao gồm đa hình là một khái niệm trong lý thuyết loại trong đó một tên có thể biểu thị trường hợp của nhiều lớp khác nhau miễn là chúng có liên quan bởi một số lớp siêu phổ biến. Sự đa hình bao gồm thường được hỗ trợ thông qua phân loại, tức là các đối tượng thuộc các loại khác nhau là có thể thay thế hoàn toàn cho các đối tượng thuộc loại khác (loại cơ sở ) và do đó có thể được xử lý thông qua giao diện chung. Ngoài ra, sự đa hình bao gồm có thể đạt được thông qua việc ép buộc loại , còn được gọi là loại đúc.
Một hình ảnh Wikipedia khác có tên là Polymorphism in object-oriented programming dường như trả lời câu hỏi của bạn rất tốt. Tham chiếu thứ hai trong bài viết này được gọi là On Understanding Types, Data Abstraction, and Polymorphism cũng đề cập đến vấn đề này một cách chi tiết.
Tính năng này trong Java đạt được, trong số các phương tiện khác, thông qua kế thừa các lớp và giao diện. Mặc dù các tính năng phụ của Java có thể không hiển nhiên trong điều kiện thừa kế mọi lúc. Lấy ví dụ các trường hợp hiệp phương sai và contravariance với Generics. Ngoài ra, mảng được Serializable và Cloneable mặc dù điều này là không rõ ràng bất cứ nơi nào trong hệ thống phân cấp loại. Cũng có thể nói rằng thông qua chuyển đổi mở rộng nguyên thủy, các kiểu số trong Java là đa hình. Và toán tử hoạt động polimorphically tùy thuộc vào toán hạng của họ.
Ở bất kỳ tỷ lệ nào, sự kế thừa đóng một vai trò quan trọng trong việc thực hiện một số tính đa hình này.
Overloading vs Overriding
phần thứ hai của bạn trong những câu hỏi có vẻ là về việc chọn thi hành một phương pháp nhất định. Rõ ràng, nếu một lớp ghi đè một phương thức và bạn tạo một cá thể của lớp đó, bạn muốn phiên bản overriden của phương thức được gọi, ngay cả khi bạn đang truy cập đối tượng thông qua tham chiếu của lớp cha.
Việc lựa chọn thực hiện phải của phương thức được thực hiện trong thời gian chạy như bạn đã chỉ rõ, bây giờ chữ ký của phương thức được gọi được quyết định tại thời gian biên dịch. Kể từ khi quá tải là về các phương pháp khác nhau có cùng tên và chữ ký khác nhau, đó là lý do tại sao người ta nói rằng lựa chọn phương pháp ghi đè xảy ra tại thời gian biên dịch.
Trọng Phương pháp lựa chọn tại thời gian biên dịch
Các Java Language Specification (JLS) trong phần 15.12 Method Invocation Expressions giải thích một cách chi tiết quá trình mà các trình biên dịch sau để lựa chọn phương pháp đúng để gọi.
Ở đó, bạn sẽ nhận thấy rằng đây là nhiệm vụ biên dịch. JLS nói trong tiểu mục 15.12.2:
bước này sử dụng tên của phương pháp và các loại đối số biểu thức để xác định vị trí các phương pháp đó đều dễ tiếp cận và áp dụng Có thể có nhiều hơn một phương pháp như vậy, trong trường hợp đó, một phương pháp cụ thể nhất được chọn.
Để xác minh tính chất biên dịch thời gian này, bạn có thể làm bài kiểm tra sau đây.
Khai báo lớp như thế này và biên dịch nó.
public class ChooseMethod {
public void doSomething(Number n){
System.out.println("Number");
}
}
Khai báo lớp thứ hai gọi phương thức đầu tiên và biên dịch nó.
public class MethodChooser {
public static void main(String[] args) {
ChooseMethod m = new ChooseMethod();
m.doSomething(10);
}
}
Nếu bạn gọi chính, đầu ra cho biết Number
.
Bây giờ, thêm phương thức quá tải cụ thể hơn vào lớp ChooseMethod
và biên dịch lại (nhưng không biên dịch lại lớp khác).
public void doSomething(Integer i) {
System.out.println("Integer");
}
Nếu bạn chạy lại chính, đầu ra vẫn là Number
.
Về cơ bản, bởi vì nó đã được quyết định tại thời gian biên dịch. Nếu bạn biên dịch lại các lớp MethodChooser
(một với chính), và chạy chương trình một lần nữa, đầu ra sẽ là Integer
.
Như vậy, nếu bạn muốn ép buộc chọn một trong các phương thức quá tải, loại đối số phải tương ứng với loại tham số tại thời gian biên dịch và không chỉ ở thời gian chạy.
Trọng Phương pháp lựa chọn tại Run thời gian
Một lần nữa, chữ ký của phương pháp này được quyết định tại thời gian biên dịch, nhưng việc thực hiện thực tế được quyết định tại thời gian chạy.
Khai báo lớp như thế này và biên dịch nó.
public class ChooseMethodA {
public void doSomething(Number n){
System.out.println("Number A");
}
}
Sau đó khai báo một lớp mở rộng thứ hai và biên dịch:
public class ChooseMethodB extends ChooseMethodA { }
Và trong lớp MethodChooser bạn làm:
public class MethodChooser {
public static void main(String[] args) {
ChooseMethodA m = new ChooseMethodB();
m.doSomething(10);
}
}
Và nếu bạn chạy nó, bạn nhận được đầu ra Number A
, và đây là Ok, vì phương thức này không được ghi đè trong ChooseMethodB
và do đó việc thực thi được gọi là của ChooseMethodA
.
Bây giờ, thêm một phương pháp overriden trong MethodChooserB
:
public void doSomething(Number n){
System.out.println("Number B");
}
Và biên dịch lại chỉ thế này, và chạy các phương pháp chính một lần nữa.
Bây giờ, bạn sẽ có được sản lượng Number B
Như vậy, việc thực hiện đã được lựa chọn trong thời gian chạy, và không phải biên dịch lại của lớp MethodChooser
được yêu cầu.
Tôi nghĩ quá tải không liên quan gì đến tính đa hình. – ka3ak
Trên thực tế đây là dạng pollymorphism: Từ Wikipedia: thuật ngữ đa hình ad-hoc để chỉ các hàm đa hình có thể được áp dụng cho các đối số của các kiểu khác nhau, nhưng hoạt động khác nhau tùy thuộc vào loại đối số mà chúng được áp dụng (còn được gọi là quá tải hàm hoặc quá tải toán tử) .. –
@edalorzo: độ chính xác thực tế của http://en.wikipedia.org/wiki/Ad-hoc_polymorphism bị tranh chấp. – Jayan