Ghi đè các phương thức bằng cách chuyển làm đối số lớp đối tượng trong đó siêu kiểu được mong đợi


12

Tôi chỉ đang học Java và không phải là một lập trình viên thực hành.

Cuốn sách tôi đang theo dõi nói rằng khi ghi đè một phương thức, các kiểu đối số phải giống nhau, nhưng các kiểu trả về có thể tương thích đa hình.

Câu hỏi của tôi là tại sao các đối số được truyền cho phương thức ghi đè không phải là một loại lớp con của loại siêu dự kiến?

Trong phương thức quá tải, bất kỳ phương thức nào tôi gọi trên đối tượng đều được đảm bảo được xác định trên đối tượng.


Lưu ý về các bản sao được đề xuất:

Các gợi ý đầu tiên dường như là về hệ thống phân cấp lớp và nơi để đặt chức năng. Câu hỏi của tôi tập trung hơn vào lý do tại sao hạn chế ngôn ngữ tồn tại.

Các gợi ý thứ hai giải thích làm thế nào để làm những gì tôi đang hỏi, nhưng không phải lý do tại sao nó phải được thực hiện theo cách đó. Câu hỏi của tôi là tập trung vào lý do tại sao.


1
đã hỏi và trả lời trong câu hỏi trước : "Điều này thường được coi là không tuân thủ Nguyên tắc thay thế Liskov (LSP), bởi vì các ràng buộc được thêm vào khiến các hoạt động của lớp cơ sở không phải lúc nào cũng phù hợp với lớp dẫn xuất ..."
gnat

1
có thể trùng lặp với việc tạo lớp con cụ thể hơn với

@gnat câu hỏi không phải là liệu yêu cầu các kiểu con cụ thể hơn cho các đối số có vi phạm một số nguyên tắc máy tính hay không, câu hỏi là liệu nó có khả thi hay không, mà edalorzo đã trả lời.
user949300

Câu trả lời:


18

Khái niệm ban đầu bạn đề cập đến trong câu hỏi của bạn được gọi là các kiểu trả về covariant .

Các kiểu trả về covariant hoạt động vì một phương thức được cho là trả về một đối tượng thuộc loại nhất định và các phương thức ghi đè có thể thực sự trả về một lớp con của nó. Dựa trên các quy tắc phân nhóm của một ngôn ngữ như Java, nếu Slà một kiểu con T, thì bất cứ nơi nào Txuất hiện chúng ta đều có thể vượt qua S.

Vì vậy, an toàn để trả về một Skhi ghi đè một phương thức dự kiến ​​a T.

Đề xuất của bạn để chấp nhận rằng một phương thức ghi đè sử dụng các đối số là các kiểu con của các phương thức được yêu cầu bởi phương thức được ghi đè phức tạp hơn nhiều vì nó dẫn đến sự không rõ ràng trong hệ thống loại.

Một mặt, bởi các quy tắc phân nhóm tương tự được đề cập ở trên, rất có thể nó đã hoạt động cho những gì bạn muốn làm. Ví dụ

interface Hunter {
   public void hunt(Animal animal);
}

Không có gì ngăn cản việc triển khai của lớp này nhận được bất kỳ loại động vật nào, vì như vậy nó đã đáp ứng các tiêu chí trong câu hỏi của bạn.

Nhưng giả sử chúng ta có thể ghi đè phương thức này như bạn đề xuất:

class MammutHunter implements Hunter {
  @Override
  public void hunt(Mammut animal) {
  }
}

Đây là phần thú vị, bây giờ bạn có thể làm điều này:

AnimalHunter hunter = new MammutHunter();
hunter.hunt(new Bear()); //Uh oh

Theo giao diện công cộng của AnimalHunterbạn sẽ có thể săn bất kỳ động vật nào, nhưng theo việc thực hiện của MammutHunterbạn, bạn chỉ chấp nhậnMammut các đối tượng. Do đó phương pháp overriden không thỏa mãn giao diện chung. Chúng tôi chỉ phá vỡ sự lành mạnh của hệ thống loại ở đây.

Bạn có thể thực hiện những gì bạn muốn bằng cách sử dụng thuốc generic.

interface AnimalHunter<T extends Animal> {
   void hunt(T animal);
}

Sau đó, bạn có thể xác định MammutHunter của bạn

class MammutHunter implements AnimalHunter<Mammut> {
   void hunt(Mammut m){
   }
}

Và sử dụng hiệp phương sai chung và chống chỉ định, bạn có thể thư giãn các quy tắc có lợi cho bạn khi cần thiết. Chẳng hạn, chúng ta có thể chắc chắn rằng một thợ săn động vật có vú chỉ có thể săn những con mèo trong một bối cảnh nhất định:

AnimalHunter<? super Feline> hunter = new MammalHunter();
hunter.hunt(new Lion());
hunter.hunt(new Puma());

Giả sử MammalHunterthực hiệnAnimalHunter<Mammal> .

Trong trường hợp đó, điều này sẽ không được chấp nhận:

hunter.hunt(new Mammut()):

Ngay cả khi động vật có vú là động vật có vú, nó sẽ không được chấp nhận do những hạn chế đối với loại chống chỉ định mà chúng tôi đang sử dụng ở đây. Vì vậy, bạn vẫn có thể thực hiện một số điều khiển qua các loại để làm những việc như những gì bạn đã đề cập.


3

Vấn đề không phải là những gì bạn làm trong phương thức ghi đè với đối tượng được đưa ra làm đối số. Vấn đề là loại đối số mà mã sử dụng phương thức của bạn được phép cung cấp cho phương thức của bạn là gì.

Một phương thức ghi đè phải thực hiện hợp đồng của phương thức ghi đè. Nếu phương thức được ghi đè chấp nhận đối số của một số lớp và bạn ghi đè lên nó bằng một phương thức chỉ chấp nhận một lớp con, thì nó không hoàn thành hợp đồng. Đó là lý do tại sao nó không hợp lệ.

Ghi đè một phương thức với một loại đối số cụ thể hơn được gọi là nạp chồng . Nó định nghĩa một phương thức có cùng tên nhưng với một kiểu đối số khác hoặc cụ thể hơn. Một phương thức quá tải có sẵn bên cạnh phương thức ban đầu. Phương thức nào được gọi phụ thuộc vào loại được biết tại thời điểm biên dịch. Để quá tải một phương thức, bạn phải bỏ chú thích @Override.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.