2012-03-28 39 views
5

Cả thừa kế và đa hình có phải là mối quan hệ IS-A không? Và đúng là sự kế thừa và tính đa hình "trọng" xảy ra trong thời gian chạy trong khi sự đa hình "quá tải" xảy ra trong thời gian biên dịch? Lý do tôi hỏi điều này là bởi vì rất nhiều diễn đàn dường như đưa ra các câu trả lời mâu thuẫn và thường gây nhầm lẫn.Thừa kế và đa hình Java

Cảm ơn!

+3

Tôi nghĩ quá tải không liên quan gì đến tính đa hình. – ka3ak

+0

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ử) .. –

+0

@edalorzo: độ chính xác thực tế của http://en.wikipedia.org/wiki/Ad-hoc_polymorphism bị tranh chấp. – Jayan

Trả lời

8

Đố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ápcá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.

+2

Câu trả lời của Jayan khá ngắn gọn, tôi nghĩ vậy. – shiva

0

Đa hình là tác động của thừa kế. Nó chỉ có thể xảy ra trong các lớp học mở rộng lẫn nhau.

Đa hình xảy ra khi chạy; Tôi chưa bao giờ nghe nói về "quá tải đa hình".

Inheritance xảy ra tại thời gian biên dịch, lúc này bạn viết:

class A extends B 
{ 
} 
0

Chỉ thừa kế tạo thành một IS-A mối quan hệ. đa hình không liên quan gì đến nó.

"quá tải" là ví dụ về đa hình. Bạn có thể tìm hiểu thêm về thời gian chạy và thời gian biên dịch đa hình here

1

Tôi nghĩ bạn nói đúng.

Đa hình xem xét kiểu thời gian chạy của đối tượng để quyết định phương thức thực thi và lựa chọn phương thức quá tải để gọi không được quyết định trong thời gian chạy, tùy thuộc vào loại tham số tại thời gian biên dịch.

5

Đa hình: Khả năng của các đối tượng khác nhau để nhận cùng một thông điệp và phản hồi khác nhau.

Thừa kế một cách để đạt được điều đó, nhưng không cần thiết. Xem Duck Typing

Quá tải phương thức là 'trình biên dịch cú pháp thời gian biên dịch' — vì mọi phương thức đều có chữ ký duy nhất sau khi biên dịch. Không thực sự bất cứ điều gì để làm với đa hình.

0

• Thừa kế xác định mối quan hệ cha-con giữa hai lớp, đa hình lợi dụng mối quan hệ đó để thêm hành vi động trong mã của bạn.

• Thừa kế khuyến khích sử dụng lại mã bằng cách cho phép lớp con kế thừa hành vi từ lớp cha. Mặt khác, Polymorphism cho phép trẻ xác định lại hành vi đã được xác định bên trong lớp cha. Không có đa hình, không thể cho trẻ thực hiện hành vi của chính nó khi được biểu diễn bằng biến tham chiếu của Phụ huynh, nhưng với Đa hình có thể được thực hiện.

• Java không cho phép đa thừa kế các lớp nhưng cho phép đa thừa kế Giao diện, thực sự cần thiết để triển khai Đa hình. Ví dụ, một lớp có thể là Runnable, Comparator và Serializable cùng một lúc vì cả ba đều là các giao diện. Điều này làm cho chúng vượt qua xung quanh trong mã, ví dụ: bạn có thể chuyển một thể hiện của lớp này đến một phương thức chấp nhận Serializable, hoặc tới Collections.sort() chấp nhận một Comparator.

• Cả Đa hình và Thừa kế đều cho phép các chương trình hướng đối tượng phát triển. Ví dụ, bằng cách sử dụng Thừa kế, bạn có thể xác định các kiểu người dùng mới trong Hệ thống xác thực và bằng cách sử dụng Đa hình, bạn có thể tận dụng mã xác thực đã được viết. Kể từ khi, Thừa kế đảm bảo hành vi lớp cơ sở tối thiểu, một phương pháp tùy thuộc vào siêu lớp hoặc siêu giao diện vẫn có thể chấp nhận một đối tượng của lớp cơ sở và có thể xác thực nó.