JavaRush /Blog Java /Random-VI /Đa kế thừa trong Java. Thành phần so với kế thừa
DSergey_Kh
Mức độ

Đa kế thừa trong Java. Thành phần so với kế thừa

Xuất bản trong nhóm

Đa kế thừa trong Java

Đa kế thừa cho phép bạn tạo một lớp kế thừa từ nhiều siêu lớp. Không giống như một số ngôn ngữ lập trình hướng đối tượng phổ biến khác, chẳng hạn như C++, Java không cho phép đa kế thừa từ các lớp. Java không hỗ trợ kế thừa nhiều lớp vì nó có thể dẫn đến vấn đề kim cương. Và thay vì tìm cách giải quyết vấn đề này, có những lựa chọn tốt hơn để chúng ta có thể đạt được kết quả tương tự như đa kế thừa.

Vấn đề kim cương

Để hiểu vấn đề kim cương dễ dàng hơn, hãy giả sử rằng đa kế thừa được hỗ trợ trong Java. Trong trường hợp này, chúng ta có thể có hệ thống phân cấp lớp như trong hình bên dưới. Đa kế thừa trong Java.  Thành phần và Kế thừa - 1Giả sử rằng lớp này SuperClasslà trừu tượng và một số phương thức được khai báo trong đó. Cả hai lớp cụ thể ClassAClassB.
package com.journaldev.inheritance;
public abstract class SuperClass {
	public abstract void doSomething();
}
package com.journaldev.inheritance;
public class ClassA extends SuperClass{
	@Override
	public void doSomething(){
		System.out.println("doSomething implementation of A");
	}
	//ClassA own method
	public void methodA(){
	}
}
package com.journaldev.inheritance;
public class ClassB extends SuperClass{
	@Override
	public void doSomething(){
		System.out.println("doSomething implementation of B");
	}
	//ClassB specific method
	public void methodB(){
	}
}
Bây giờ giả sử chúng ta muốn triển khai ClassCvà kế thừa nó từ ClassAand ClassB.
package com.journaldev.inheritance;
public class ClassC extends ClassA, ClassB{
	public void test(){
		//calling super class method
		doSomething();
	}
}
Lưu ý rằng phương thức này test()gọi phương thức siêu lớp doSomething(). Điều này dẫn đến sự mơ hồ vì trình biên dịch không biết nên thực thi phương thức siêu lớp nào. Đây là sơ đồ lớp hình kim cương được gọi là bài toán kim cương. Đây là lý do chính tại sao Java không hỗ trợ đa kế thừa. Lưu ý rằng vấn đề kế thừa nhiều lớp ở trên chỉ có thể xảy ra với ba lớp có ít nhất một phương thức chung.

Kế thừa nhiều giao diện

Trong Java, tính đa kế thừa không được hỗ trợ trong các lớp nhưng nó được hỗ trợ trong các giao diện. Và một giao diện có thể mở rộng nhiều giao diện khác. Dưới đây là một ví dụ đơn giản.
package com.journaldev.inheritance;
public interface InterfaceA {
	public void doSomething();
}
package com.journaldev.inheritance;
public interface InterfaceB {
	public void doSomething();
}
Lưu ý rằng cả hai giao diện đều khai báo cùng một phương thức. Bây giờ chúng ta có thể tạo một giao diện mở rộng cả hai giao diện này, như trong ví dụ bên dưới.
package com.journaldev.inheritance;
public interface InterfaceC extends InterfaceA, InterfaceB {
	//same method is declared in InterfaceA and InterfaceB both
	public void doSomething();
}
Điều này rất hiệu quả vì các giao diện chỉ khai báo các phương thức và việc triển khai sẽ được thực hiện trong các lớp kế thừa giao diện đó. Vì vậy, không có cách nào để tránh sự mơ hồ trong kế thừa đa giao diện.
package com.journaldev.inheritance;
public class InterfacesImpl implements InterfaceA, InterfaceB, InterfaceC {
	@Override
	public void doSomething() {
		System.out.println("doSomething implementation of concrete class");
	}
	public static void main(String[] args) {
		InterfaceA objA = new InterfacesImpl();
		InterfaceB objB = new InterfacesImpl();
		InterfaceC objC = new InterfacesImpl();

		//all the method calls below are going to same concrete implementation
		objA.doSomething();
		objB.doSomething();
		objC.doSomething();
	}
}
Xin lưu ý rằng bất cứ khi nào bạn ghi đè bất kỳ phương thức siêu lớp nào hoặc triển khai một phương thức giao diện, hãy sử dụng chú thích @Override. Nếu chúng ta muốn sử dụng hàm methodA()của một lớp ClassAvà hàm methodB()của một lớp ClassBtrong một lớp thì sao ClassC? Giải pháp nằm ở việc sử dụng bố cục. Dưới đây là một phiên bản của lớp ClassCsử dụng thành phần để định nghĩa cả phương thức của lớp và phương thức doSomething()của một trong các đối tượng.
package com.journaldev.inheritance;
public class ClassC{
	ClassA objA = new ClassA();
	ClassB objB = new ClassB();
	public void test(){
		objA.doSomething();
	}
	public void methodA(){
		objA.methodA();
	}
	public void methodB(){
		objB.methodB();
	}
}

Thành phần so với kế thừa

Một trong những phương pháp lập trình Java tốt nhất là “phê duyệt thành phần trước khi kế thừa”. Chúng ta sẽ khám phá một số khía cạnh ủng hộ phương pháp này.
  1. Giả sử chúng ta có một siêu lớp và một lớp mở rộng nó:

    package com.journaldev.inheritance;
    public class ClassC{
    	public void methodC(){
    	}
    }
    package com.journaldev.inheritance;
    public class ClassD extends ClassC{
    	public int test(){
    		return 0;
    	}
    }

    Đoạn mã trên biên dịch và hoạt động tốt. Tuy nhiên, điều gì sẽ xảy ra nếu chúng ta thay đổi cách triển khai của lớp ClassCnhư dưới đây:

    package com.journaldev.inheritance;
    public class ClassC{
    	public void methodC(){
    	}
    	public void test(){
    	}
    }

    Lưu ý rằng phương thức này test()đã tồn tại trong lớp con nhưng kiểu trả về thì khác. Bây giờ lớp ClassDsẽ không biên dịch và nếu bạn sử dụng bất kỳ IDE nào, nó sẽ nhắc bạn thay đổi kiểu trả về trong siêu lớp hoặc lớp con.

    Bây giờ hãy tưởng tượng một tình huống trong đó chúng ta có hệ thống phân cấp kế thừa lớp nhiều cấp và không có quyền truy cập vào siêu lớp. Chúng ta sẽ không có lựa chọn nào khác ngoài việc thay đổi chữ ký phương thức lớp con hoặc tên của nó để loại bỏ lỗi biên dịch. Chúng ta cũng sẽ phải thay đổi phương thức của lớp con ở tất cả những nơi nó được gọi. Vì vậy, tính kế thừa làm cho mã của chúng ta dễ vỡ.

    Vấn đề trên sẽ không bao giờ xảy ra với thành phần và điều này khiến nó trở nên hấp dẫn hơn đối với tính kế thừa.

  2. Một vấn đề khác với tính kế thừa là chúng ta hiển thị tất cả các phương thức của siêu lớp cho máy khách và nếu siêu lớp của chúng ta không được thiết kế phù hợp và có các lỗ hổng bảo mật, thì ngay cả khi chúng ta triển khai cách triển khai tốt nhất cho lớp của mình, chúng ta vẫn bị ảnh hưởng bởi việc triển khai kém. của siêu lớp. Thành phần giúp chúng tôi cung cấp quyền truy cập có kiểm soát vào các phương thức siêu lớp, trong khi tính kế thừa không cung cấp quyền kiểm soát các phương thức siêu lớp. Đây cũng là một trong những lợi ích chính của việc sáng tác từ sự kế thừa.

  3. Một ưu điểm khác của bố cục là nó cho phép linh hoạt trong các phương thức gọi. Việc triển khai lớp ClassCđược trình bày ở trên của chúng tôi không phải là tối ưu và đảm bảo rằng thời gian biên dịch được gắn với phương thức sẽ được gọi. Với những thay đổi tối thiểu, chúng ta có thể làm cho cuộc gọi phương thức trở nên linh hoạt và năng động.

    package com.journaldev.inheritance;
    public class ClassC{
    	SuperClass obj = null;
    	public ClassC(SuperClass o){
    		this.obj = o;
    	}
    	public void test(){
    		obj.doSomething();
    	}
    	public static void main(String args[]){
    		ClassC obj1 = new ClassC(new ClassA());
    		ClassC obj2 = new ClassC(new ClassB());
    
    		obj1.test();
    		obj2.test();
    	}
    }

    Kết quả của chương trình trình bày ở trên:

    doSomething implementation of A
    doSomething implementation of B

    Tính linh hoạt trong việc gọi phương thức này không có sẵn với tính kế thừa, điều này mang lại một lợi ích khác cho việc lựa chọn thành phần.

  4. Kiểm thử đơn vị sẽ dễ thực hiện hơn với tính năng tổng hợp vì chúng ta biết rằng mình đang sử dụng tất cả các phương thức từ siêu lớp và có thể sao chép chúng cho quá trình kiểm thử. Trong khi đó, trong kế thừa, chúng ta phụ thuộc nhiều hơn vào siêu lớp và không biết tất cả các phương thức của siêu lớp sẽ được sử dụng. Vì vậy chúng ta phải kiểm thử tất cả các phương thức của siêu lớp, đây là công việc làm thêm do tính kế thừa.

    Lý tưởng nhất là chúng ta chỉ nên sử dụng tính kế thừa khi mối quan hệ giữa lớp con và lớp cha được định nghĩa là "is". Trong tất cả các trường hợp khác, nên sử dụng chế phẩm.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION