Java không cho phép nhiều kế thừa, nhưng nó cho phép thực hiện nhiều giao diện. Tại sao?
Java không cho phép nhiều kế thừa, nhưng nó cho phép thực hiện nhiều giao diện. Tại sao?
Câu trả lời:
Bởi vì các giao diện chỉ xác định những gì lớp đang làm, không phải nó đang làm như thế nào .
Vấn đề với nhiều kế thừa là hai lớp có thể định nghĩa các cách khác nhau để thực hiện cùng một thứ và lớp con không thể chọn cái nào để chọn.
Một trong những giảng viên đại học của tôi đã giải thích cho tôi theo cách này:
Giả sử tôi có một lớp, đó là Máy nướng bánh mì, và một lớp khác, đó là NucleBomb. Cả hai có thể có một thiết lập "bóng tối". Cả hai đều có một phương thức on (). (Một người đã tắt (), người kia thì không.) Nếu tôi muốn tạo một lớp đó là một lớp con của cả hai ... như bạn có thể thấy, đây là một vấn đề thực sự có thể xảy ra ở mặt tôi ở đây .
Vì vậy, một trong những vấn đề chính là nếu bạn có hai lớp cha, chúng có thể có các cách triển khai khác nhau của cùng một tính năng - hoặc có thể là hai tính năng khác nhau có cùng tên, như trong ví dụ của người hướng dẫn của tôi. Sau đó, bạn phải đối phó với việc quyết định lớp con nào sẽ sử dụng. Chắc chắn có nhiều cách để xử lý việc này - C ++ làm như vậy - nhưng các nhà thiết kế của Java cảm thấy rằng điều này sẽ khiến mọi thứ trở nên quá phức tạp.
Mặc dù vậy, với một giao diện, bạn đang mô tả một cái gì đó mà lớp có khả năng thực hiện, thay vì mượn phương thức làm của một lớp khác. Nhiều giao diện ít có khả năng gây ra xung đột phức tạp cần được giải quyết hơn là nhiều lớp cha.
Bởi vì tính kế thừa được sử dụng quá mức ngay cả khi bạn không thể nói "này, phương pháp đó có vẻ hữu ích, tôi cũng sẽ mở rộng lớp đó".
public class MyGodClass extends AppDomainObject, HttpServlet, MouseAdapter,
AbstractTableModel, AbstractListModel, AbstractList, AbstractMap, ...
Câu trả lời của câu hỏi này nằm ở hoạt động bên trong của trình biên dịch java (chuỗi xây dựng). Nếu chúng ta thấy hoạt động nội bộ của trình biên dịch java:
public class Bank {
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank{
public void printBankBalance(){
System.out.println("20k");
}
}
Sau khi biên dịch, giao diện này giống như:
public class Bank {
public Bank(){
super();
}
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank {
SBI(){
super();
}
public void printBankBalance(){
System.out.println("20k");
}
}
Khi chúng ta mở rộng lớp và tạo một đối tượng của nó, một chuỗi hàm tạo sẽ chạy cho đến Object
lớp.
Mã trên sẽ chạy tốt. nhưng nếu chúng ta có một lớp khác được gọi là lớp Car
mở rộng Bank
và một lớp lai (nhiều kế thừa) được gọi là SBICar
:
class Car extends Bank {
Car() {
super();
}
public void run(){
System.out.println("99Km/h");
}
}
class SBICar extends Bank, Car {
SBICar() {
super(); //NOTE: compile time ambiguity.
}
public void run() {
System.out.println("99Km/h");
}
public void printBankBalance(){
System.out.println("20k");
}
}
Trong trường hợp này (SBICar) sẽ không tạo được chuỗi xây dựng ( biên dịch thời gian mơ hồ ).
Đối với các giao diện, điều này được cho phép bởi vì chúng ta không thể tạo một đối tượng của nó.
Đối với khái niệm mới default
và static
phương pháp vui lòng tham khảo mặc định trong giao diện .
Hy vọng điều này sẽ giải quyết câu hỏi của bạn. Cảm ơn.
Việc triển khai nhiều giao diện rất hữu ích và không gây ra nhiều vấn đề cho người thực hiện ngôn ngữ cũng như lập trình viên. Vì vậy, nó được cho phép. Nhiều kế thừa trong khi cũng hữu ích, có thể gây ra vấn đề nghiêm trọng cho người dùng (sợ hãi kim cương ). Và hầu hết những điều bạn làm với nhiều kế thừa cũng có thể được thực hiện bằng cách sáng tác hoặc sử dụng các lớp bên trong. Vì vậy, nhiều kế thừa bị cấm mang lại nhiều vấn đề hơn lợi ích.
ToyotaCar
và HybridCar
cả hai xuất phát từ Car
và overrode Car.Drive
, và nếu PriusCar
được thừa hưởng cả hai nhưng không ghi đèDrive
, hệ thống sẽ không có cách nào để xác định những gì ảo Car.Drive
nên làm. Các giao diện tránh vấn đề này bằng cách tránh các điều kiện in nghiêng ở trên.
void UseCar(Car &foo)
; không thể được dự kiến sẽ bao gồm sự định hướng giữa ToyotaCar::Drive
và HybridCar::Drive
(vì nó thường không biết và cũng không quan tâm rằng những loại khác thậm chí còn tồn tại ). Một ngôn ngữ có thể, như C ++ không, yêu cầu mã với ToyotaCar &myCar
nhu cầu để vượt qua nó để UseCar
phải cast đầu tiên cho một trong hai HybridCar
hoặc ToyotaCar
, nhưng kể từ khi ((Xe) (HybridCar) myCar) .Drive` và ((Car)(ToyotaCar)myCar).Drive
sẽ làm những việc khác nhau, mà có ngụ ý rằng upcasts không bảo tồn danh tính.
Bạn có thể tìm thấy câu trả lời chính xác cho truy vấn này trong trang tài liệu oracle về nhiều kế thừa
Nhiều kế thừa trạng thái: Khả năng kế thừa các trường từ nhiều lớp
Một lý do tại sao ngôn ngữ lập trình Java không cho phép bạn mở rộng nhiều hơn một lớp là để tránh các vấn đề về thừa kế trạng thái, đó là khả năng kế thừa các trường từ nhiều lớp
Nếu nhiều kế thừa được cho phép và Khi bạn tạo một đối tượng bằng cách khởi tạo lớp đó, đối tượng đó sẽ kế thừa các trường từ tất cả các siêu lớp của lớp. Nó sẽ gây ra hai vấn đề.
Đa kế thừa thực hiện: Khả năng kế thừa các định nghĩa phương thức từ nhiều lớp
Các vấn đề với cách tiếp cận này: tên xung đột ts và sự mơ hồ . Nếu một lớp con và siêu lớp chứa cùng tên phương thức (và chữ ký), trình biên dịch không thể xác định phiên bản nào sẽ gọi.
Nhưng java hỗ trợ kiểu thừa kế đa dạng này với các phương thức mặc định , đã được giới thiệu kể từ khi phát hành Java 8. Trình biên dịch Java cung cấp một số quy tắc để xác định phương thức mặc định mà một lớp cụ thể sử dụng.
Tham khảo bài viết dưới đây của SE để biết thêm chi tiết về cách giải quyết vấn đề kim cương:
Sự khác biệt giữa các lớp trừu tượng và giao diện trong Java 8 là gì?
Nhiều kiểu thừa kế: Khả năng của một lớp để thực hiện nhiều giao diện.
Vì giao diện không chứa các trường có thể thay đổi, bạn không phải lo lắng về các vấn đề phát sinh từ nhiều kế thừa trạng thái ở đây.
Người ta nói rằng trạng thái đối tượng được liên quan đến các trường trong đó và nó sẽ trở nên mơ hồ nếu có quá nhiều lớp được kế thừa. Đây là liên kết
http://docs.oracle.com/javase/tutorial/java/IandI/mult Môninherribution.html
Java chỉ hỗ trợ nhiều kế thừa thông qua các giao diện. Một lớp có thể thực hiện bất kỳ số lượng giao diện nào nhưng chỉ có thể mở rộng một lớp.
Nhiều kế thừa không được hỗ trợ vì nó dẫn đến vấn đề kim cương chết người. Tuy nhiên, nó có thể được giải quyết nhưng nó dẫn đến hệ thống phức tạp nên nhiều kế thừa đã bị các nhà sáng lập Java bỏ.
Trong một tờ giấy trắng có tựa đề Java Java: Tổng quan về James của James Gosling vào tháng 2 năm 1995 ( liên kết ) đưa ra một ý tưởng về lý do tại sao nhiều kế thừa không được hỗ trợ trong Java.
Theo Gosling:
"JAVA bỏ qua nhiều tính năng hiếm khi được sử dụng, hiểu kém, khó hiểu của C ++ mà theo kinh nghiệm của chúng tôi mang lại nhiều đau buồn hơn lợi ích. Điều này chủ yếu bao gồm quá tải toán tử (mặc dù nó có quá tải phương thức), nhiều kế thừa và ép buộc tự động mở rộng."
Vì lý do tương tự, C # không cho phép nhiều kế thừa nhưng cho phép bạn triển khai nhiều giao diện.
Bài học rút ra từ C ++ với nhiều sự kế thừa là nó dẫn đến nhiều vấn đề hơn giá trị của nó.
Một giao diện là một hợp đồng của những thứ mà lớp của bạn phải thực hiện. Bạn không đạt được bất kỳ chức năng nào từ giao diện. Kế thừa cho phép bạn kế thừa chức năng của lớp cha (và trong đa kế thừa, điều đó có thể trở nên cực kỳ khó hiểu).
Cho phép nhiều giao diện cho phép bạn sử dụng Mẫu thiết kế (như Bộ điều hợp) để giải quyết các loại vấn đề giống nhau mà bạn có thể giải quyết bằng cách sử dụng nhiều kế thừa, nhưng theo cách đáng tin cậy và dễ đoán hơn nhiều.
D1
và D2
đều kế thừa từ B
, và mỗi ghi đè một chức năng f
, và nếu obj
là một thể hiện của một kiểu S
mà thừa hưởng từ cả hai D1
và D2
nhưng không ghi đè lên f
, sau đó đúc một tham chiếu đến S
để D1
nên mang một cái gì đó mà f
sử dụng các D1
ghi đè và đúc để B
không nên thay đổi điều đó. Tương tự, truyền tham chiếu S
để D2
mang lại một cái gì đó f
sử dụng D2
ghi đè và truyền để B
không thay đổi điều đó. Nếu một ngôn ngữ không cần cho phép thêm thành viên ảo ...
Vì chủ đề này không gần nên tôi sẽ đăng câu trả lời này, tôi hy vọng điều này sẽ giúp ai đó hiểu tại sao java không cho phép nhiều kế thừa.
Hãy xem xét các lớp sau:
public class Abc{
public void doSomething(){
}
}
Trong trường hợp này, lớp Abc không mở rộng được gì phải không? Không quá nhanh, lớp này ngầm mở rộng lớp Object, lớp cơ sở cho phép mọi thứ hoạt động trong java. Tất cả mọi thứ là một đối tượng.
Nếu bạn cố gắng sử dụng các lớp ở trên, bạn sẽ thấy rằng IDE của bạn cho phép bạn sử dụng các phương pháp như: equals(Object o)
, toString()
, vv, nhưng bạn đã không khai báo các phương pháp đó, họ đến từ các lớp cơ sởObject
Bạn có thể thử:
public class Abc extends String{
public void doSomething(){
}
}
Điều này là tốt, bởi vì lớp học của bạn sẽ không ngầm kéo dài Object
mà sẽ mở rộng String
vì bạn đã nói nó. Hãy xem xét sự thay đổi sau đây:
public class Abc{
public void doSomething(){
}
@Override
public String toString(){
return "hello";
}
}
Bây giờ lớp của bạn sẽ luôn trả về "xin chào" nếu bạn gọi toString ().
Bây giờ hãy tưởng tượng lớp sau:
public class Flyer{
public void makeFly(){
}
}
public class Bird extends Abc, Flyer{
public void doAnotherThing(){
}
}
Một lần nữa lớp Flyer
ngầm mở rộng Object có phương thức toString()
, bất kỳ lớp nào cũng sẽ có phương thức này vì tất cả chúng đều mở rộng Object
gián tiếp, vì vậy, nếu bạn gọi toString()
từ Bird
, toString()
java nào sẽ phải sử dụng? Từ Abc
hay Flyer
? Điều này sẽ xảy ra với bất kỳ lớp nào cố gắng mở rộng hai hoặc nhiều lớp, để tránh loại "xung đột phương thức" này, họ đã xây dựng ý tưởng về giao diện , về cơ bản bạn có thể nghĩ chúng là một lớp trừu tượng không mở rộng đối tượng một cách gián tiếp . Vì chúng là trừu tượng nên chúng sẽ phải được thực hiện bởi một lớp, đó là một đối tượng(bạn không thể kích hoạt giao diện một mình, chúng phải được thực hiện bởi một lớp), vì vậy mọi thứ sẽ tiếp tục hoạt động tốt.
Để khác các lớp từ giao diện, từ khóa thực hiện chỉ dành riêng cho giao diện.
Bạn có thể triển khai bất kỳ giao diện nào bạn thích trong cùng một lớp vì chúng không mở rộng bất cứ thứ gì theo mặc định (nhưng bạn có thể tạo giao diện mở rộng giao diện khác, nhưng một lần nữa, giao diện "cha" sẽ không mở rộng Object "), vì vậy giao diện là chỉ là một giao diện và họ sẽ không phải chịu " dấu hiệu chữ ký phương thức ", nếu họ thực hiện trình biên dịch sẽ đưa ra cảnh báo cho bạn và bạn sẽ phải thay đổi chữ ký phương thức để sửa nó (chữ ký = tên phương thức + params + kiểu trả về) .
public interface Flyer{
public void makeFly(); // <- method without implementation
}
public class Bird extends Abc implements Flyer{
public void doAnotherThing(){
}
@Override
public void makeFly(){ // <- implementation of Flyer interface
}
// Flyer does not have toString() method or any method from class Object,
// no method signature collision will happen here
}
Bởi vì một giao diện chỉ là một hợp đồng. Và một lớp thực sự là một thùng chứa dữ liệu.
Ví dụ hai lớp A, B có cùng phương thức m1 (). Và lớp C mở rộng cả A, B.
class C extends A, B // for explaining purpose.
Bây giờ, lớp C sẽ tìm kiếm định nghĩa của m1. Đầu tiên, nó sẽ tìm kiếm trong lớp nếu nó không tìm thấy thì nó sẽ kiểm tra lớp phụ huynh. Cả A, B đều có định nghĩa Vì vậy, ở đây sự mơ hồ xảy ra nên chọn định nghĩa nào. Vì vậy, JAVA KHÔNG HPORT TRỢ NHIỀU NỀN TẢNG.
Java không hỗ trợ nhiều kế thừa vì hai lý do:
Object
lớp. Khi nó kế thừa từ nhiều hơn một siêu lớp, lớp con có được sự mơ hồ để có được thuộc tính của lớp Object ..super()
để gọi hàm tạo của lớp supper. Nếu lớp có nhiều hơn một siêu lớp, nó sẽ bị lẫn lộn.Vì vậy, khi một lớp mở rộng từ nhiều hơn một siêu lớp, chúng ta sẽ gặp lỗi thời gian biên dịch.
Lấy ví dụ như trường hợp Class A có phương thức getS Something và lớp B có phương thức getS Something và lớp C mở rộng A và B. Điều gì sẽ xảy ra nếu ai đó gọi là C.getS Something? Không có cách nào để xác định phương thức nào để gọi.
Các giao diện về cơ bản chỉ xác định các phương thức mà lớp triển khai cần chứa. Một lớp thực hiện nhiều giao diện chỉ có nghĩa là lớp đó phải thực hiện các phương thức từ tất cả các giao diện đó. Whci sẽ không dẫn đến bất kỳ vấn đề như mô tả ở trên.
Hãy xem xét một kịch bản trong đó Test1, Test2 và Test3 là ba lớp. Lớp Test3 kế thừa các lớp Test2 và Test1. Nếu các lớp Test1 và Test2 có cùng một phương thức và bạn gọi nó từ đối tượng lớp con, sẽ có sự mơ hồ khi gọi phương thức của lớp Test1 hoặc Test2 nhưng không có sự mơ hồ nào về giao diện như trong giao diện không có triển khai.
Java không hỗ trợ nhiều kế thừa, đa đường và thừa kế lai do vấn đề không rõ ràng:
Scenario for multiple inheritance: Let us take class A , class B , class C. class A has alphabet(); method , class B has also alphabet(); method. Now class C extends A, B and we are creating object to the subclass i.e., class C , so C ob = new C(); Then if you want call those methods ob.alphabet(); which class method takes ? is class A method or class B method ? So in the JVM level ambiguity problem occurred. Thus Java does not support multiple inheritance.
Liên kết tham khảo: https://plus.google.com/u/0/cransities/102217496457095083679
theo cách đơn giản, tất cả chúng ta đều biết, chúng ta có thể kế thừa (mở rộng) một lớp nhưng chúng ta có thể thực hiện rất nhiều giao diện .. đó là vì trong các giao diện chúng ta không đưa ra một triển khai chỉ nói chức năng. giả sử nếu java có thể mở rộng rất nhiều lớp và những lớp đó có cùng phương thức .. thì ở điểm này nếu chúng ta cố gắng gọi phương thức siêu lớp trong lớp con thì phương thức nào giả sử để chạy ??, trình biên dịch lấy ví dụ nhầm lẫn : - thử nhiều lần mở rộng nhưng trong giao diện những phương thức không có cơ thể chúng ta nên thực hiện những phương thức trong lớp con .. cố gắng thực hiện nhiều công cụ để không phải lo lắng ..
* Đây là một câu trả lời đơn giản vì tôi là người mới bắt đầu sử dụng Java *
Hãy xem xét có ba lớp X
, Y
và Z
.
Vì vậy, chúng tôi đang kế thừa like X extends Y, Z
Và cả hai Y
và Z
đang có một phương thức alphabet()
có cùng kiểu trả về và đối số. Phương pháp này alphabet()
trong Y
nói với hiển thị bảng chữ cái đầu tiên và phương pháp bảng chữ cái trong Z
nói hiển thị bảng chữ cái cuối cùng . Vì vậy, ở đây mơ hồ khi alphabet()
được gọi bởi X
. Cho dù nó nói để hiển thị bảng chữ cái đầu tiên hoặc cuối cùng ??? Vì vậy, java không hỗ trợ nhiều kế thừa. Trong trường hợp Giao diện, hãy xem xét Y
và Z
như giao diện. Vì vậy, cả hai sẽ chứa khai báo phương thức alphabet()
nhưng không định nghĩa. Nó sẽ không cho biết liệu hiển thị bảng chữ cái đầu tiên hoặc bảng chữ cái cuối cùng hoặc bất cứ điều gì nhưng sẽ chỉ tuyên bố một phương thứcalphabet()
. Vì vậy, không có lý do để nâng cao sự mơ hồ. Chúng ta có thể định nghĩa phương thức với bất cứ thứ gì chúng ta muốn trong lớp X
.
Vì vậy, trong một từ, trong định nghĩa Giao diện được thực hiện sau khi thực hiện để không nhầm lẫn.